elFinderVolumeDriver::getContents()   A
last analyzed

Complexity

Conditions 4
Paths 4

Size

Total Lines 17
Code Lines 9

Duplication

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

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

namespace YourVendor;

class YourClass { }

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

Loading history...
15
	
16
	/**
17
	 * Request args
18
	 * $_POST or $_GET values
19
	 * 
20
	 * @var array
21
	 */
22
	protected $ARGS = array();
23
	
24
	/**
25
	 * Driver id
26
	 * Must be started from letter and contains [a-z0-9]
27
	 * Used as part of volume id
28
	 *
29
	 * @var string
30
	 **/
31
	protected $driverId = 'a';
32
	
33
	/**
34
	 * Volume id - used as prefix for files hashes
35
	 *
36
	 * @var string
37
	 **/
38
	protected $id = '';
39
	
40
	/**
41
	 * Flag - volume "mounted" and available
42
	 *
43
	 * @var bool
44
	 **/
45
	protected $mounted = false;
46
	
47
	/**
48
	 * Root directory path
49
	 *
50
	 * @var string
51
	 **/
52
	protected $root = '';
53
	
54
	/**
55
	 * Root basename | alias
56
	 *
57
	 * @var string
58
	 **/
59
	protected $rootName = '';
60
	
61
	/**
62
	 * Default directory to open
63
	 *
64
	 * @var string
65
	 **/
66
	protected $startPath = '';
67
	
68
	/**
69
	 * Base URL
70
	 *
71
	 * @var string
72
	 **/
73
	protected $URL = '';
74
	
75
	/**
76
	 * Thumbnails dir path
77
	 *
78
	 * @var string
79
	 **/
80
	protected $tmbPath = '';
81
	
82
	/**
83
	 * Is thumbnails dir writable
84
	 *
85
	 * @var bool
86
	 **/
87
	protected $tmbPathWritable = false;
88
	
89
	/**
90
	 * Thumbnails base URL
91
	 *
92
	 * @var string
93
	 **/
94
	protected $tmbURL = '';
95
	
96
	/**
97
	 * Thumbnails size in px
98
	 *
99
	 * @var int
100
	 **/
101
	protected $tmbSize = 48;
102
	
103
	/**
104
	 * Image manipulation lib name
105
	 * auto|imagick|mogtify|gd
106
	 *
107
	 * @var string
108
	 **/
109
	protected $imgLib = 'auto';
110
	
111
	/**
112
	 * Library to crypt files name
113
	 *
114
	 * @var string
115
	 **/
116
	protected $cryptLib = '';
117
	
118
	/**
119
	 * Archivers config
120
	 *
121
	 * @var array
122
	 **/
123
	protected $archivers = array(
124
		'create'  => array(),
125
		'extract' => array()
126
	);
127
	
128
	/**
129
	 * Server character encoding
130
	 *
131
	 * @var string or null
132
	 **/
133
	protected $encoding = null;
134
	
135
	/**
136
	 * How many subdirs levels return for tree
137
	 *
138
	 * @var int
139
	 **/
140
	protected $treeDeep = 1;
141
	
142
	/**
143
	 * Errors from last failed action
144
	 *
145
	 * @var array
146
	 **/
147
	protected $error = array();
148
	
149
	/**
150
	 * Today 24:00 timestamp
151
	 *
152
	 * @var int
153
	 **/
154
	protected $today = 0;
155
	
156
	/**
157
	 * Yesterday 24:00 timestamp
158
	 *
159
	 * @var int
160
	 **/
161
	protected $yesterday = 0;
162
	
163
	/**
164
	 * Force make dirctory on extract
165
	 *
166
	 * @var int
167
	 **/
168
	protected $extractToNewdir = 'auto';
169
	
170
	/**
171
	 * Object configuration
172
	 *
173
	 * @var array
174
	 **/
175
	protected $options = array(
176
		'id'              => '',
177
		// root directory path
178
		'path'            => '',
179
		// open this path on initial request instead of root path
180
		'startPath'       => '',
181
		// how many subdirs levels return per request
182
		'treeDeep'        => 1,
183
		// root url, not set to disable sending URL to client (replacement for old "fileURL" option)
184
		'URL'             => '',
185
		// directory separator. required by client to show paths correctly
186
		'separator'       => DIRECTORY_SEPARATOR,
187
		// Server character encoding (default is '': UTF-8)
188
		'encoding'        => '',
189
		// for convert character encoding (default is '': Not change locale)
190
		'locale'          => '',
191
		// URL of volume icon (16x16 pixel image file)
192
		'icon'            => '',
193
		// CSS Class of volume root in tree
194
		'rootCssClass'    => '',
195
		// Search timeout (sec)
196
		'searchTimeout'   => 30,
197
		// library to crypt/uncrypt files names (not implemented)
198
		'cryptLib'        => '',
199
		// how to detect files mimetypes. (auto/internal/finfo/mime_content_type)
200
		'mimeDetect'      => 'auto',
201
		// mime.types file path (for mimeDetect==internal)
202
		'mimefile'        => '',
203
		// mime type normalize map : Array '[ext]:[detected mime type]' => '[normalized mime]'
204
		'mimeMap'         => array(
205
		                     'md:application/x-genesis-rom' => 'text/x-markdown',
206
		                     'md:text/plain'                => 'text/x-markdown',
207
		                     'markdown:text/plain'          => 'text/x-markdown',
208
		                     'css:text/x-asm'               => 'text/css'
209
		                    ),
210
		// MIME regex of send HTTP header "Content-Disposition: inline"
211
		// '.' is allow inline of all of MIME types
212
		// '$^' is not allow inline of all of MIME types
213
		'dispInlineRegex' => '^(?:(?:image|text)|application/x-shockwave-flash$)',
214
		// directory for thumbnails
215
		'tmbPath'         => '.tmb',
216
		// mode to create thumbnails dir
217
		'tmbPathMode'     => 0777,
218
		// thumbnails dir URL. Set it if store thumbnails outside root directory
219
		'tmbURL'          => '',
220
		// thumbnails size (px)
221
		'tmbSize'         => 48,
222
		// thumbnails crop (true - crop, false - scale image to fit thumbnail size)
223
		'tmbCrop'         => true,
224
		// thumbnails background color (hex #rrggbb or 'transparent')
225
		'tmbBgColor'      => '#ffffff',
226
		// image manipulations library
227
		'imgLib'          => 'auto',
228
		// Jpeg image saveing quality
229
		'jpgQuality'      => 100,
230
		// on paste file -  if true - old file will be replaced with new one, if false new file get name - original_name-number.ext
231
		'copyOverwrite'   => true,
232
		// if true - join new and old directories content on paste
233
		'copyJoin'        => true,
234
		// on upload -  if true - old file will be replaced with new one, if false new file get name - original_name-number.ext
235
		'uploadOverwrite' => true,
236
		// mimetypes allowed to upload
237
		'uploadAllow'     => array(),
238
		// mimetypes not allowed to upload
239
		'uploadDeny'      => array(),
240
		// order to proccess uploadAllow and uploadDeny options
241
		'uploadOrder'     => array('deny', 'allow'),
242
		// maximum upload file size. NOTE - this is size for every uploaded files
243
		'uploadMaxSize'   => 0,
244
		// files dates format
245
		'dateFormat'      => 'j M Y H:i',
246
		// files time format
247
		'timeFormat'      => 'H:i',
248
		// if true - every folder will be check for children folders, otherwise all folders will be marked as having subfolders
249
		'checkSubfolders' => true,
250
		// allow to copy from this volume to other ones?
251
		'copyFrom'        => true,
252
		// allow to copy from other volumes to this one?
253
		'copyTo'          => true,
254
		// list of commands disabled on this root
255
		'disabled'        => array(),
256
		// enable file owner, group & mode info, `false` to inactivate "chmod" command.
257
		'statOwner'       => false,
258
		// allow exec chmod of read-only files
259
		'allowChmodReadOnly' => false,
260
		// regexp or function name to validate new file name
261
		'acceptedName'    => '/^[^\.].*/', //<-- DONT touch this! Use constructor options to overwrite it!
262
		// function/class method to control files permissions
263
		'accessControl'   => null,
264
		// some data required by access control
265
		'accessControlData' => null,
266
		// default permissions.
267
		'defaults'     => array(
268
			'read'   => true,
269
			'write'  => true,
270
			'locked' => false,
271
			'hidden' => false
272
		),
273
		// files attributes
274
		'attributes'   => array(),
275
		// Allowed archive's mimetypes to create. Leave empty for all available types.
276
		'archiveMimes' => array(),
277
		// Manual config for archivers. See example below. Leave empty for auto detect
278
		'archivers'    => array(),
279
		// plugin settings
280
		'plugin'       => array(),
281
		// Is support parent directory time stamp update on add|remove|rename item
282
		// Default `null` is auto detection that is LocalFileSystem, FTP or Dropbox are `true`
283
		'syncChkAsTs'  => null,
284
		// Long pooling sync checker function for syncChkAsTs is true
285
		// Calls with args (TARGET DIRCTORY PATH, STAND-BY(sec), OLD TIMESTAMP, VOLUME DRIVER INSTANCE, ELFINDER INSTANCE)
286
		// This function must return the following values. Changed: New Timestamp or Same: Old Timestamp or Error: false
287
		// Default `null` is try use elFinderVolumeLocalFileSystem::localFileSystemInotify() on LocalFileSystem driver
288
		// another driver use elFinder stat() checker
289
		'syncCheckFunc'=> null,
290
		// Long polling sync stand-by time (sec)
291
		'plStandby'    => 30,
292
		// Sleep time (sec) for elFinder stat() checker (syncChkAsTs is true)
293
		'tsPlSleep'    => 10,
294
		// Sleep time (sec) for elFinder ls() checker (syncChkAsTs is false)
295
		'lsPlSleep'    => 30,
296
		// Client side sync interval minimum (ms)
297
		// Default `null` is auto set to ('tsPlSleep' or 'lsPlSleep') * 1000
298
		// `0` to disable auto sync
299
		'syncMinMs'    => null,
300
		// required to fix bug on macos
301
		'utf8fix'      => false,
302
		 //                           й                 ё              Й               Ё              Ø         Å
303
		'utf8patterns' => array("\u0438\u0306", "\u0435\u0308", "\u0418\u0306", "\u0415\u0308", "\u00d8A", "\u030a"),
304
		'utf8replace'  => array("\u0439",        "\u0451",       "\u0419",       "\u0401",       "\u00d8", "\u00c5")
305
	);
306
307
	/**
308
	 * Defaults permissions
309
	 *
310
	 * @var array
311
	 **/
312
	protected $defaults = array(
313
		'read'   => true,
314
		'write'  => true,
315
		'locked' => false,
316
		'hidden' => false
317
	);
318
	
319
	/**
320
	 * Access control function/class
321
	 *
322
	 * @var mixed
323
	 **/
324
	protected $attributes = array();
325
	
326
	/**
327
	 * Access control function/class
328
	 *
329
	 * @var mixed
330
	 **/
331
	protected $access = null;
332
	
333
	/**
334
	 * Mime types allowed to upload
335
	 *
336
	 * @var array
337
	 **/
338
	protected $uploadAllow = array();
339
	
340
	/**
341
	 * Mime types denied to upload
342
	 *
343
	 * @var array
344
	 **/
345
	protected $uploadDeny = array();
346
	
347
	/**
348
	 * Order to validate uploadAllow and uploadDeny
349
	 *
350
	 * @var array
351
	 **/
352
	protected $uploadOrder = array();
353
	
354
	/**
355
	 * Maximum allowed upload file size.
356
	 * Set as number or string with unit - "10M", "500K", "1G"
357
	 *
358
	 * @var int|string
359
	 **/
360
	protected $uploadMaxSize = 0;
361
	
362
	/**
363
	 * Mimetype detect method
364
	 *
365
	 * @var string
366
	 **/
367
	protected $mimeDetect = 'auto';
368
	
369
	/**
370
	 * Flag - mimetypes from externail file was loaded
371
	 *
372
	 * @var bool
373
	 **/
374
	private static $mimetypesLoaded = false;
375
	
376
	/**
377
	 * Finfo object for mimeDetect == 'finfo'
378
	 *
379
	 * @var object
380
	 **/
381
	protected $finfo = null;
382
	
383
	/**
384
	 * List of disabled client's commands
385
	 *
386
	 * @var array
387
	 **/
388
	protected $disabled = array();
389
	
390
	/**
391
	 * default extensions/mimetypes for mimeDetect == 'internal' 
392
	 *
393
	 * @var array
394
	 **/
395
	protected static $mimetypes = array(
396
		// applications
397
		'ai'    => 'application/postscript',
398
		'eps'   => 'application/postscript',
399
		'exe'   => 'application/x-executable',
400
		'doc'   => 'application/msword',
401
		'dot'   => 'application/msword',
402
		'xls'   => 'application/vnd.ms-excel',
403
		'xlt'   => 'application/vnd.ms-excel',
404
		'xla'   => 'application/vnd.ms-excel',
405
		'ppt'   => 'application/vnd.ms-powerpoint',
406
		'pps'   => 'application/vnd.ms-powerpoint',
407
		'pdf'   => 'application/pdf',
408
		'xml'   => 'application/xml',
409
		'swf'   => 'application/x-shockwave-flash',
410
		'torrent' => 'application/x-bittorrent',
411
		'jar'   => 'application/x-jar',
412
		// open office (finfo detect as application/zip)
413
		'odt'   => 'application/vnd.oasis.opendocument.text',
414
		'ott'   => 'application/vnd.oasis.opendocument.text-template',
415
		'oth'   => 'application/vnd.oasis.opendocument.text-web',
416
		'odm'   => 'application/vnd.oasis.opendocument.text-master',
417
		'odg'   => 'application/vnd.oasis.opendocument.graphics',
418
		'otg'   => 'application/vnd.oasis.opendocument.graphics-template',
419
		'odp'   => 'application/vnd.oasis.opendocument.presentation',
420
		'otp'   => 'application/vnd.oasis.opendocument.presentation-template',
421
		'ods'   => 'application/vnd.oasis.opendocument.spreadsheet',
422
		'ots'   => 'application/vnd.oasis.opendocument.spreadsheet-template',
423
		'odc'   => 'application/vnd.oasis.opendocument.chart',
424
		'odf'   => 'application/vnd.oasis.opendocument.formula',
425
		'odb'   => 'application/vnd.oasis.opendocument.database',
426
		'odi'   => 'application/vnd.oasis.opendocument.image',
427
		'oxt'   => 'application/vnd.openofficeorg.extension',
428
		// MS office 2007 (finfo detect as application/zip)
429
		'docx'  => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
430
		'docm'  => 'application/vnd.ms-word.document.macroEnabled.12',
431
		'dotx'  => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
432
		'dotm'  => 'application/vnd.ms-word.template.macroEnabled.12',
433
		'xlsx'  => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
434
		'xlsm'  => 'application/vnd.ms-excel.sheet.macroEnabled.12',
435
		'xltx'  => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
436
		'xltm'  => 'application/vnd.ms-excel.template.macroEnabled.12',
437
		'xlsb'  => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
438
		'xlam'  => 'application/vnd.ms-excel.addin.macroEnabled.12',
439
		'pptx'  => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
440
		'pptm'  => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
441
		'ppsx'  => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
442
		'ppsm'  => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12',
443
		'potx'  => 'application/vnd.openxmlformats-officedocument.presentationml.template',
444
		'potm'  => 'application/vnd.ms-powerpoint.template.macroEnabled.12',
445
		'ppam'  => 'application/vnd.ms-powerpoint.addin.macroEnabled.12',
446
		'sldx'  => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
447
		'sldm'  => 'application/vnd.ms-powerpoint.slide.macroEnabled.12',
448
		// archives
449
		'gz'    => 'application/x-gzip',
450
		'tgz'   => 'application/x-gzip',
451
		'bz'    => 'application/x-bzip2',
452
		'bz2'   => 'application/x-bzip2',
453
		'tbz'   => 'application/x-bzip2',
454
		'xz'    => 'application/x-xz',
455
		'zip'   => 'application/zip',
456
		'rar'   => 'application/x-rar',
457
		'tar'   => 'application/x-tar',
458
		'7z'    => 'application/x-7z-compressed',
459
		// texts
460
		'txt'   => 'text/plain',
461
		'php'   => 'text/x-php',
462
		'html'  => 'text/html',
463
		'htm'   => 'text/html',
464
		'js'    => 'text/javascript',
465
		'css'   => 'text/css',
466
		'rtf'   => 'text/rtf',
467
		'rtfd'  => 'text/rtfd',
468
		'py'    => 'text/x-python',
469
		'java'  => 'text/x-java-source',
470
		'rb'    => 'text/x-ruby',
471
		'sh'    => 'text/x-shellscript',
472
		'pl'    => 'text/x-perl',
473
		'xml'   => 'text/xml',
474
		'sql'   => 'text/x-sql',
475
		'c'     => 'text/x-csrc',
476
		'h'     => 'text/x-chdr',
477
		'cpp'   => 'text/x-c++src',
478
		'hh'    => 'text/x-c++hdr',
479
		'log'   => 'text/plain',
480
		'csv'   => 'text/x-comma-separated-values',
481
		'md'    => 'text/x-markdown',
482
		'markdown' => 'text/x-markdown',
483
		// images
484
		'bmp'   => 'image/x-ms-bmp',
485
		'jpg'   => 'image/jpeg',
486
		'jpeg'  => 'image/jpeg',
487
		'gif'   => 'image/gif',
488
		'png'   => 'image/png',
489
		'tif'   => 'image/tiff',
490
		'tiff'  => 'image/tiff',
491
		'tga'   => 'image/x-targa',
492
		'psd'   => 'image/vnd.adobe.photoshop',
493
		'ai'    => 'image/vnd.adobe.photoshop',
494
		'xbm'   => 'image/xbm',
495
		'pxm'   => 'image/pxm',
496
		//audio
497
		'mp3'   => 'audio/mpeg',
498
		'mid'   => 'audio/midi',
499
		'ogg'   => 'audio/ogg',
500
		'oga'   => 'audio/ogg',
501
		'm4a'   => 'audio/x-m4a',
502
		'wav'   => 'audio/wav',
503
		'wma'   => 'audio/x-ms-wma',
504
		// video
505
		'avi'   => 'video/x-msvideo',
506
		'dv'    => 'video/x-dv',
507
		'mp4'   => 'video/mp4',
508
		'mpeg'  => 'video/mpeg',
509
		'mpg'   => 'video/mpeg',
510
		'mov'   => 'video/quicktime',
511
		'wm'    => 'video/x-ms-wmv',
512
		'flv'   => 'video/x-flv',
513
		'mkv'   => 'video/x-matroska',
514
		'webm'  => 'video/webm',
515
		'ogv'   => 'video/ogg',
516
		'ogm'   => 'video/ogg'
517
		);
518
	
519
	/**
520
	 * Directory separator - required by client
521
	 *
522
	 * @var string
523
	 **/
524
	protected $separator = DIRECTORY_SEPARATOR;
525
	
526
	/**
527
	 * System Root path (Unix like: '/', Windows: '\', 'C:\' or 'D:\'...)
528
	 *
529
	 * @var string
530
	 **/
531
	protected $systemRoot = DIRECTORY_SEPARATOR;
532
	
533
	/**
534
	 * Mimetypes allowed to display
535
	 *
536
	 * @var array
537
	 **/
538
	protected $onlyMimes = array();
539
	
540
	/**
541
	 * Store files moved or overwrited files info
542
	 *
543
	 * @var array
544
	 **/
545
	protected $removed = array();
546
	
547
	/**
548
	 * Cache storage
549
	 *
550
	 * @var array
551
	 **/
552
	protected $cache = array();
553
	
554
	/**
555
	 * Cache by folders
556
	 *
557
	 * @var array
558
	 **/
559
	protected $dirsCache = array();
560
	
561
	/**
562
	 * Cache for subdirsCE()
563
	 * 
564
	 * @var array
565
	 */
566
	protected $subdirsCache = array();
567
	
568
	/**
569
	 * Reference of $_SESSION[elFinder::$sessionCacheKey][$this->id]
570
	 * 
571
	 * @var array
572
	 */
573
	protected $sessionCache;
574
	
575
	
576
	/**
577
	 * Search start time
578
	 * 
579
	 * @var int
580
	 */
581
	protected $searchStart;
582
	
583
	/*********************************************************************/
584
	/*                            INITIALIZATION                         */
585
	/*********************************************************************/
586
	
587
	/**
588
	 * Prepare driver before mount volume.
589
	 * Return true if volume is ready.
590
	 *
591
	 * @return bool
592
	 * @author Dmitry (dio) Levashov
593
	 **/
594
	protected function init() {
595
		return true;
596
	}	
597
		
598
	/**
599
	 * Configure after successfull mount.
600
	 * By default set thumbnails path and image manipulation library.
601
	 *
602
	 * @return void
603
	 * @author Dmitry (dio) Levashov
604
	 **/
605
	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...
606
		// set ARGS
607
		$this->ARGS = $_SERVER['REQUEST_METHOD'] === 'POST'? $_POST : $_GET;
608
		// set thumbnails path
609
		$path = $this->options['tmbPath'];
610
		if ($path) {
611 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...
612
				if (@mkdir($path)) {
613
					chmod($path, $this->options['tmbPathMode']);
614
				} else {
615
					$path = '';
616
				}
617
			} 
618
			
619
			if (is_dir($path) && is_readable($path)) {
620
				$this->tmbPath = $path;
621
				$this->tmbPathWritable = is_writable($path);
622
			}
623
		}
624
625
		// set image manipulation library
626
		$type = preg_match('/^(imagick|gd|auto)$/i', $this->options['imgLib'])
627
			? strtolower($this->options['imgLib'])
628
			: 'auto';
629
630
		if (($type == 'imagick' || $type == 'auto') && extension_loaded('imagick')) {
631
			$this->imgLib = 'imagick';
632
		} else {
633
			$this->imgLib = function_exists('gd_info') ? 'gd' : '';
634
		}
635
		
636
		// check 'statOwner' for command `chmod`
637
		if (empty($this->options['statOwner'])) {
638
			$this->disabled[] ='chmod';
639
		}
640
		
641
		// check 'mimeMap'
642
		if (!is_array($this->options['mimeMap'])) {
643
			$this->options['mimeMap'] = array();
644
		}
645
	}
646
	
647
	protected function sessionRestart() {
0 ignored issues
show
Coding Style introduced by
sessionRestart 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...
648
		$start = @session_start();
649
		if (!isset($_SESSION[elFinder::$sessionCacheKey])) {
650
			$_SESSION[elFinder::$sessionCacheKey] = array();
651
		}
652
		$this->sessionCache = &$_SESSION[elFinder::$sessionCacheKey][$this->id];
653
		return $start;
654
	}
655
	/*********************************************************************/
656
	/*                              PUBLIC API                           */
657
	/*********************************************************************/
658
	
659
	/**
660
	 * Return driver id. Used as a part of volume id.
661
	 *
662
	 * @return string
663
	 * @author Dmitry (dio) Levashov
664
	 **/
665
	public function driverId() {
666
		return $this->driverId;
667
	}
668
	
669
	/**
670
	 * Return volume id
671
	 *
672
	 * @return string
673
	 * @author Dmitry (dio) Levashov
674
	 **/
675
	public function id() {
676
		return $this->id;
677
	}
678
		
679
	/**
680
	 * Return debug info for client
681
	 *
682
	 * @return array
683
	 * @author Dmitry (dio) Levashov
684
	 **/
685
	public function debug() {
686
		return array(
687
			'id'         => $this->id(),
688
			'name'       => strtolower(substr(get_class($this), strlen('elfinderdriver'))),
689
			'mimeDetect' => $this->mimeDetect,
690
			'imgLib'     => $this->imgLib
691
		);
692
	}
693
694
	/**
695
	 * chmod a file or folder
696
	 *
697
	 * @param  string   $hash    file or folder hash to chmod
698
	 * @param  string   $mode    octal string representing new permissions
699
	 * @return array|false
700
	 * @author David Bartle
701
	 **/
702
	public function chmod($hash, $mode) {
703
		if ($this->commandDisabled('chmod')) {
704
			return $this->setError(elFinder::ERROR_PERM_DENIED);
705
		}
706
707
		if (!($file = $this->file($hash))) {
708
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
709
		}
710
711
		if (!$this->options['allowChmodReadOnly']) {
712 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...
713
				return $this->setError(elFinder::ERROR_PERM_DENIED, $file['name']);
714
			}
715
		}
716
717
		$path = $this->decode($hash);
718
719 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...
720
			return $this->setError(elFinder::ERROR_PERM_DENIED, $file['name']);
721
		}
722
723
		$this->clearcache();
724
725
		if ($file = $this->stat($path)) {
726
			$files = array($file);
727
			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...
728
				foreach ($this->getScandir($path) as $stat) {
729
					if ($this->mimeAccepted($stat['mime'])) {
730
						$files[] = $stat;
731
					}
732
				}
733
			}
734
			return $files;
735
		} else {
736
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
737
		}
738
	}
739
	
740
	/**
741
	 * stat a file or folder for elFinder cmd exec
742
	 *
743
	 * @param  string   $hash    file or folder hash to chmod
744
	 * @return array
745
	 * @author Naoki Sawada
746
	 **/
747
	public function fstat($hash) {
748
		$path = $this->decode($hash);
749
		return $this->stat($path);
750
	}
751
	
752
	
753
	public function clearstatcache() {
754
		clearstatcache();
755
		$this->cache = $this->dirsCache = array();
756
	}
757
	
758
	/**
759
	 * "Mount" volume.
760
	 * Return true if volume available for read or write, 
761
	 * false - otherwise
762
	 *
763
	 * @return bool
764
	 * @author Dmitry (dio) Levashov
765
	 * @author Alexey Sukhotin
766
	 **/
767
	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...
768
		if (!isset($opts['path']) || $opts['path'] === '') {
769
			return $this->setError('Path undefined.');;
770
		}
771
		
772
		$this->options = array_merge($this->options, $opts);
773
		$this->id = $this->driverId.(!empty($this->options['id']) ? $this->options['id'] : elFinder::$volumesCnt++).'_';
774
		$this->root = $this->normpathCE($this->options['path']);
775
		$this->separator = isset($this->options['separator']) ? $this->options['separator'] : DIRECTORY_SEPARATOR;
776
		$this->systemRoot = isset($this->options['systemRoot']) ? $this->options['systemRoot'] : $this->separator;
777
		
778
		// set server encoding
779
		if (!empty($this->options['encoding']) && strtoupper($this->options['encoding']) !== 'UTF-8') {
780
			$this->encoding = $this->options['encoding'];
781
		} else {
782
			$this->encoding = null;
783
		}
784
		
785
		$argInit = !empty($this->ARGS['init']);
786
		
787
		// session cache
788
		if ($argInit || ! isset($_SESSION[elFinder::$sessionCacheKey][$this->id])) {
789
			$_SESSION[elFinder::$sessionCacheKey][$this->id] = array();
790
		}
791
		$this->sessionCache = &$_SESSION[elFinder::$sessionCacheKey][$this->id];
792
		
793
		// default file attribute
794
		$this->defaults = array(
795
			'read'    => isset($this->options['defaults']['read'])  ? !!$this->options['defaults']['read']  : true,
796
			'write'   => isset($this->options['defaults']['write']) ? !!$this->options['defaults']['write'] : true,
797
			'locked'  => isset($this->options['defaults']['locked']) ? !!$this->options['defaults']['locked'] : false,
798
			'hidden'  => isset($this->options['defaults']['hidden']) ? !!$this->options['defaults']['hidden'] : false
799
		);
800
801
		// root attributes
802
		$this->attributes[] = array(
803
			'pattern' => '~^'.preg_quote(DIRECTORY_SEPARATOR).'$~',
804
			'locked'  => true,
805
			'hidden'  => false
806
		);
807
		// set files attributes
808 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...
809
			
810
			foreach ($this->options['attributes'] as $a) {
811
				// attributes must contain pattern and at least one rule
812
				if (!empty($a['pattern']) || count($a) > 1) {
813
					$this->attributes[] = $a;
814
				}
815
			}
816
		}
817
818
		if (!empty($this->options['accessControl']) && is_callable($this->options['accessControl'])) {
819
			$this->access = $this->options['accessControl'];
820
		}
821
		
822
		$this->today     = mktime(0,0,0, date('m'), date('d'), date('Y'));
823
		$this->yesterday = $this->today-86400;
824
		
825
		// 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...
826
		if (!$this->init()) {
827
			return false;
828
		}
829
		
830
		// check some options is arrays
831
		$this->uploadAllow = isset($this->options['uploadAllow']) && is_array($this->options['uploadAllow'])
832
			? $this->options['uploadAllow']
833
			: array();
834
			
835
		$this->uploadDeny = isset($this->options['uploadDeny']) && is_array($this->options['uploadDeny'])
836
			? $this->options['uploadDeny']
837
			: array();
838
839
		if (is_string($this->options['uploadOrder'])) { // telephat_mode on, compatibility with 1.x
840
			$parts = explode(',', isset($this->options['uploadOrder']) ? $this->options['uploadOrder'] : 'deny,allow');
841
			$this->uploadOrder = array(trim($parts[0]), trim($parts[1]));
842
		} else { // telephat_mode off
843
			$this->uploadOrder = $this->options['uploadOrder'];
844
		}
845
			
846
		if (!empty($this->options['uploadMaxSize'])) {
847
			$size = ''.$this->options['uploadMaxSize'];
848
			$unit = strtolower(substr($size, strlen($size) - 1));
849
			$n = 1;
850
			switch ($unit) {
851
				case 'k':
852
					$n = 1024;
853
					break;
854
				case 'm':
855
					$n = 1048576;
856
					break;
857
				case 'g':
858
					$n = 1073741824;
859
			}
860
			$this->uploadMaxSize = intval($size)*$n;
861
		}
862
		// Set maximum to PHP_INT_MAX
863
		if (!defined('PHP_INT_MAX')) {
864
			define('PHP_INT_MAX', 2147483647);
865
		}
866
		if ($this->uploadMaxSize < 1 || $this->uploadMaxSize > PHP_INT_MAX) {
867
			$this->uploadMaxSize = PHP_INT_MAX;
868
		}
869
		
870
		$this->disabled = isset($this->options['disabled']) && is_array($this->options['disabled'])
871
			? $this->options['disabled']
872
			: array();
873
		
874
		$this->cryptLib   = $this->options['cryptLib'];
875
		$this->mimeDetect = $this->options['mimeDetect'];
876
877
		// find available mimetype detect method
878
		$type = strtolower($this->options['mimeDetect']);
879
		$type = preg_match('/^(finfo|mime_content_type|internal|auto)$/i', $type) ? $type : 'auto';
880
		$regexp = '/text\/x\-(php|c\+\+)/';
881
	
882
		if (($type == 'finfo' || $type == 'auto') 
883
		&& class_exists('finfo', false)) {
884
			$tmpFileInfo = @explode(';', @finfo_file(finfo_open(FILEINFO_MIME), __FILE__));
885
		} else {
886
			$tmpFileInfo = false;
887
		}
888
	
889
		if ($tmpFileInfo && preg_match($regexp, array_shift($tmpFileInfo))) {
890
			$type = 'finfo';
891
			$this->finfo = finfo_open(FILEINFO_MIME);
892
		} elseif (($type == 'mime_content_type' || $type == 'auto') 
893
		&& function_exists('mime_content_type')
894
		&& 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...
895
			$type = 'mime_content_type';
896
		} else {
897
			$type = 'internal';
898
		}
899
		$this->mimeDetect = $type;
900
901
		// load mimes from external file for mimeDetect == 'internal'
902
		// based on Alexey Sukhotin idea and patch: http://elrte.org/redmine/issues/163
903
		// file must be in file directory or in parent one 
904
		if ($this->mimeDetect == 'internal' && !self::$mimetypesLoaded) {
905
			self::$mimetypesLoaded = true;
906
			$this->mimeDetect = 'internal';
907
			$file = false;
908
			if (!empty($this->options['mimefile']) && file_exists($this->options['mimefile'])) {
909
				$file = $this->options['mimefile'];
910
			} elseif (file_exists(dirname(__FILE__).DIRECTORY_SEPARATOR.'mime.types')) {
911
				$file = dirname(__FILE__).DIRECTORY_SEPARATOR.'mime.types';
912
			} elseif (file_exists(dirname(dirname(__FILE__)).DIRECTORY_SEPARATOR.'mime.types')) {
913
				$file = dirname(dirname(__FILE__)).DIRECTORY_SEPARATOR.'mime.types';
914
			}
915
916 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...
917
				$mimecf = file($file);
918
919
				foreach ($mimecf as $line_num => $line) {
920
					if (!preg_match('/^\s*#/', $line)) {
921
						$mime = preg_split('/\s+/', $line, -1, PREG_SPLIT_NO_EMPTY);
922
						for ($i = 1, $size = count($mime); $i < $size ; $i++) {
923
							if (!isset(self::$mimetypes[$mime[$i]])) {
924
								self::$mimetypes[$mime[$i]] = $mime[0];
925
							}
926
						}
927
					}
928
				}
929
			}
930
		}
931
932
		$this->rootName = empty($this->options['alias']) ? $this->basenameCE($this->root) : $this->options['alias'];
933
934
		// This get's triggered if $this->root == '/' and alias is empty.
935
		// Maybe modify _basename instead?
936
		if ($this->rootName === '') $this->rootName = $this->separator;
937
938
		$root = $this->stat($this->root);
939
		
940
		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...
941
			return $this->setError('Root folder does not exists.');
942
		}
943
		if (!$root['read'] && !$root['write']) {
944
			return $this->setError('Root folder has not read and write permissions.');
945
		}
946
		
947
		// 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...
948
		
949
		if ($root['read']) {
950
			// check startPath - path to open by default instead of root
951
			$startPath = $this->options['startPath']? $this->normpathCE($this->options['startPath']) : '';
952
			if ($startPath) {
953
				$start = $this->stat($startPath);
954
				if (!empty($start)
955
				&& $start['mime'] == 'directory'
956
				&& $start['read']
957
				&& empty($start['hidden'])
958
				&& $this->inpathCE($startPath, $this->root)) {
959
					$this->startPath = $startPath;
960
					if (substr($this->startPath, -1, 1) == $this->options['separator']) {
961
						$this->startPath = substr($this->startPath, 0, -1);
962
					}
963
				}
964
			}
965
		} else {
966
			$this->options['URL']     = '';
967
			$this->options['tmbURL']  = '';
968
			$this->options['tmbPath'] = '';
969
			// read only volume
970
			array_unshift($this->attributes, array(
971
				'pattern' => '/.*/',
972
				'read'    => false
973
			));
974
		}
975
		$this->treeDeep = $this->options['treeDeep'] > 0 ? (int)$this->options['treeDeep'] : 1;
976
		$this->tmbSize  = $this->options['tmbSize'] > 0 ? (int)$this->options['tmbSize'] : 48;
977
		$this->URL      = $this->options['URL'];
978
		if ($this->URL && preg_match("|[^/?&=]$|", $this->URL)) {
979
			$this->URL .= '/';
980
		}
981
982
		$this->tmbURL   = !empty($this->options['tmbURL']) ? $this->options['tmbURL'] : '';
983
		if ($this->tmbURL && preg_match("|[^/?&=]$|", $this->tmbURL)) {
984
			$this->tmbURL .= '/';
985
		}
986
		
987
		$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...
988
			? $this->options['acceptedName']
989
			: '';
990
991
		$this->_checkArchivers();
992
		// manual control archive types to create
993 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...
994
			foreach ($this->archivers['create'] as $mime => $v) {
995
				if (!in_array($mime, $this->options['archiveMimes'])) {
996
					unset($this->archivers['create'][$mime]);
997
				}
998
			}
999
		}
1000
		
1001
		// manualy add archivers
1002 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...
1003
			foreach ($this->options['archivers']['create'] as $mime => $conf) {
1004
				if (strpos($mime, 'application/') === 0 
1005
				&& !empty($conf['cmd']) 
1006
				&& isset($conf['argc']) 
1007
				&& !empty($conf['ext'])
1008
				&& !isset($this->archivers['create'][$mime])) {
1009
					$this->archivers['create'][$mime] = $conf;
1010
				}
1011
			}
1012
		}
1013
		
1014 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...
1015
			foreach ($this->options['archivers']['extract'] as $mime => $conf) {
1016
				if (strpos($mime, 'application/') === 0
1017
				&& !empty($conf['cmd']) 
1018
				&& isset($conf['argc']) 
1019
				&& !empty($conf['ext'])
1020
				&& !isset($this->archivers['extract'][$mime])) {
1021
					$this->archivers['extract'][$mime] = $conf;
1022
				}
1023
			}
1024
		}
1025
1026
		$this->configure();
1027
		
1028
		// fix sync interval
1029 View Code Duplication
		if ($this->options['syncMinMs'] !== 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1030
			$this->options['syncMinMs'] = max($this->options[$this->options['syncChkAsTs']? 'tsPlSleep' : 'lsPlSleep'] * 1000, intval($this->options['syncMinMs']));
1031
		}
1032
1033
		return $this->mounted = true;
1034
	}
1035
	
1036
	/**
1037
	 * Some "unmount" stuffs - may be required by virtual fs
1038
	 *
1039
	 * @return void
1040
	 * @author Dmitry (dio) Levashov
1041
	 **/
1042
	public function umount() {
1043
	}
1044
	
1045
	/**
1046
	 * Return error message from last failed action
1047
	 *
1048
	 * @return array
1049
	 * @author Dmitry (dio) Levashov
1050
	 **/
1051
	public function error() {
1052
		return $this->error;
1053
	}
1054
	
1055
	/**
1056
	 * Return is uploadable that given file name 
1057
	 *
1058
	 * @param  string  $name  file name
1059
	 * @param  bool    $allowUnknown
1060
	 * @return bool
1061
	 * @author Naoki Sawada
1062
	 **/
1063
	public function isUploadableByName($name, $allowUnknown = true) {
1064
		$mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name);
1065
		return (($allowUnknown && $mimeByName === 'unknown') || $this->allowPutMime($mimeByName));
1066
	}
1067
	
1068
	/**
1069
	 * Return Extention/MIME Table (elFinderVolumeDriver::$mimetypes)
1070
	 * 
1071
	 * @return array
1072
	 * @author Naoki Sawada
1073
	 */
1074
	public function getMimeTable() {
1075
		return elFinderVolumeDriver::$mimetypes;
1076
	}
1077
	
1078
	/**
1079
	 * Set mimetypes allowed to display to client
1080
	 *
1081
	 * @param  array  $mimes
1082
	 * @return void
1083
	 * @author Dmitry (dio) Levashov
1084
	 **/
1085
	public function setMimesFilter($mimes) {
1086
		if (is_array($mimes)) {
1087
			$this->onlyMimes = $mimes;
1088
		}
1089
	}
1090
	
1091
	/**
1092
	 * Return root folder hash
1093
	 *
1094
	 * @return string
1095
	 * @author Dmitry (dio) Levashov
1096
	 **/
1097
	public function root() {
1098
		return $this->encode($this->root);
1099
	}
1100
	
1101
	/**
1102
	 * Return target path hash
1103
	 * 
1104
	 * @param  string $path
1105
	 * @param  string $name
1106
	 * @author Naoki Sawada
1107
	 */
1108
	public function getHash($path, $name = '') {
1109
		if ($name !== '') {
1110
			$path = $this->joinPathCE($path, $name);
1111
		}
1112
		return $this->encode($path);
1113
	}
1114
	
1115
	/**
1116
	 * Return root or startPath hash
1117
	 *
1118
	 * @return string
1119
	 * @author Dmitry (dio) Levashov
1120
	 **/
1121
	public function defaultPath() {
1122
		return $this->encode($this->startPath ? $this->startPath : $this->root);
1123
	}
1124
		
1125
	/**
1126
	 * Return volume options required by client:
1127
	 *
1128
	 * @return array
1129
	 * @author Dmitry (dio) Levashov
1130
	 **/
1131
	public function options($hash) {
1132
		$create = $createext = array();
1133
		if (isset($this->archivers['create']) && is_array($this->archivers['create'])) {
1134
			foreach($this->archivers['create'] as $m => $v) {
1135
				$create[] = $m;
1136
				$createext[$m] = $v['ext'];
1137
			}
1138
		}
1139
		return array(
1140
			'path'            => $this->path($hash),
1141
			'url'             => $this->URL,
1142
			'tmbUrl'          => $this->tmbURL,
1143
			'disabled'        => array_merge(array_unique($this->disabled)), // `array_merge` for type array of JSON
1144
			'separator'       => $this->separator,
1145
			'copyOverwrite'   => intval($this->options['copyOverwrite']),
1146
			'uploadOverwrite' => intval($this->options['uploadOverwrite']),
1147
			'uploadMaxSize'   => intval($this->uploadMaxSize),
1148
			'dispInlineRegex' => $this->options['dispInlineRegex'],
1149
			'jpgQuality'      => intval($this->options['jpgQuality']),
1150
			'archivers'       => array(
1151
				'create'    => $create,
1152
				'extract'   => isset($this->archivers['extract']) && is_array($this->archivers['extract']) ? array_keys($this->archivers['extract']) : array(),
1153
				'createext' => $createext
1154
			),
1155
			'uiCmdMap'        => (isset($this->options['uiCmdMap']) && is_array($this->options['uiCmdMap']))? $this->options['uiCmdMap'] : array(),
1156
			'syncChkAsTs'     => intval($this->options['syncChkAsTs']),
1157
			'syncMinMs'       => intval($this->options['syncMinMs'])
1158
		);
1159
	}
1160
	
1161
	/**
1162
	 * Get option value of this volume
1163
	 * 
1164
	 * @param string $name  target option name
1165
	 * @return NULL|mixed   target option value
1166
	 * @author Naoki Sawada
1167
	 */
1168
	public function getOption($name) {
1169
		return isset($this->options[$name])? $this->options[$name] : null;
1170
	}
1171
	
1172
	/**
1173
	 * Get plugin values of this options
1174
	 * 
1175
	 * @param string $name  Plugin name
1176
	 * @return NULL|array   Plugin values
1177
	 * @author Naoki Sawada
1178
	 */
1179
	public function getOptionsPlugin($name = '') {
1180
		if ($name) {
1181
			return isset($this->options['plugin'][$name])? $this->options['plugin'][$name] : array();
1182
		} else {
1183
			return $this->options['plugin'];
1184
		}
1185
	}
1186
	
1187
	/**
1188
	 * Return true if command disabled in options
1189
	 *
1190
	 * @param  string  $cmd  command name
1191
	 * @return bool
1192
	 * @author Dmitry (dio) Levashov
1193
	 **/
1194
	public function commandDisabled($cmd) {
1195
		return in_array($cmd, $this->disabled);
1196
	}
1197
	
1198
	/**
1199
	 * Return true if mime is required mimes list
1200
	 *
1201
	 * @param  string     $mime   mime type to check
1202
	 * @param  array      $mimes  allowed mime types list or not set to use client mimes list
1203
	 * @param  bool|null  $empty  what to return on empty list
1204
	 * @return bool|null
1205
	 * @author Dmitry (dio) Levashov
1206
	 * @author Troex Nevelin
1207
	 **/
1208
	public function mimeAccepted($mime, $mimes = null, $empty = true) {
1209
		$mimes = is_array($mimes) ? $mimes : $this->onlyMimes;
1210
		if (empty($mimes)) {
1211
			return $empty;
1212
		}
1213
		return $mime == 'directory'
1214
			|| in_array('all', $mimes)
1215
			|| in_array('All', $mimes)
1216
			|| in_array($mime, $mimes)
1217
			|| in_array(substr($mime, 0, strpos($mime, '/')), $mimes);
1218
	}
1219
	
1220
	/**
1221
	 * Return true if voume is readable.
1222
	 *
1223
	 * @return bool
1224
	 * @author Dmitry (dio) Levashov
1225
	 **/
1226
	public function isReadable() {
1227
		$stat = $this->stat($this->root);
1228
		return $stat['read'];
1229
	}
1230
	
1231
	/**
1232
	 * Return true if copy from this volume allowed
1233
	 *
1234
	 * @return bool
1235
	 * @author Dmitry (dio) Levashov
1236
	 **/
1237
	public function copyFromAllowed() {
1238
		return !!$this->options['copyFrom'];
1239
	}
1240
	
1241
	/**
1242
	 * Return file path related to root with convert encoging
1243
	 *
1244
	 * @param  string   $hash  file hash
1245
	 * @return string
1246
	 * @author Dmitry (dio) Levashov
1247
	 **/
1248
	public function path($hash) {
1249
		return $this->convEncOut($this->_path($this->convEncIn($this->decode($hash))));
1250
	}
1251
	
1252
	/**
1253
	 * Return file real path if file exists
1254
	 *
1255
	 * @param  string  $hash  file hash
1256
	 * @return string
1257
	 * @author Dmitry (dio) Levashov
1258
	 **/
1259
	public function realpath($hash) {
1260
		$path = $this->decode($hash);
1261
		return $this->stat($path) ? $path : false;
1262
	}
1263
	
1264
	/**
1265
	 * Return list of moved/overwrited files
1266
	 *
1267
	 * @return array
1268
	 * @author Dmitry (dio) Levashov
1269
	 **/
1270
	public function removed() {
1271
		return $this->removed;
1272
	}
1273
	
1274
	/**
1275
	 * Clean removed files list
1276
	 *
1277
	 * @return void
1278
	 * @author Dmitry (dio) Levashov
1279
	 **/
1280
	public function resetRemoved() {
1281
		$this->removed = array();
1282
	}
1283
	
1284
	/**
1285
	 * Return file/dir hash or first founded child hash with required attr == $val
1286
	 *
1287
	 * @param  string   $hash  file hash
1288
	 * @param  string   $attr  attribute name
1289
	 * @param  bool     $val   attribute value
1290
	 * @return string|false
1291
	 * @author Dmitry (dio) Levashov
1292
	 **/
1293
	public function closest($hash, $attr, $val) {
1294
		return ($path = $this->closestByAttr($this->decode($hash), $attr, $val)) ? $this->encode($path) : false;
1295
	}
1296
	
1297
	/**
1298
	 * Return file info or false on error
1299
	 *
1300
	 * @param  string   $hash      file hash
1301
	 * @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...
1302
	 * @return array|false
1303
	 * @author Dmitry (dio) Levashov
1304
	 **/
1305
	public function file($hash) {
1306
		$path = $this->decode($hash);
1307
		$isRoot = ($path === $this->root);
1308
		
1309
		$file = $this->stat($path);
1310
		
1311
		if ($isRoot) {
1312
			$file['uiCmdMap'] = (isset($this->options['uiCmdMap']) && is_array($this->options['uiCmdMap']))? $this->options['uiCmdMap'] : array();
1313
			$file['disabled'] = array_merge(array_unique($this->disabled)); // `array_merge` for type array of JSON
1314
			if (isset($this->options['netkey'])) {
1315
				$file['netkey'] = $this->options['netkey'];
1316
			}
1317
		}
1318
		
1319
		return ($file) ? $file : $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1320
	}
1321
	
1322
	/**
1323
	 * Return folder info
1324
	 *
1325
	 * @param  string   $hash  folder hash
1326
	 * @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...
1327
	 * @return array|false
1328
	 * @author Dmitry (dio) Levashov
1329
	 **/
1330
	public function dir($hash, $resolveLink=false) {
1331
		if (($dir = $this->file($hash)) == false) {
1332
			return $this->setError(elFinder::ERROR_DIR_NOT_FOUND);
1333
		}
1334
1335
		if ($resolveLink && !empty($dir['thash'])) {
1336
			$dir = $this->file($dir['thash']);
1337
		}
1338
		
1339
		return $dir && $dir['mime'] == 'directory' && empty($dir['hidden']) 
1340
			? $dir 
1341
			: $this->setError(elFinder::ERROR_NOT_DIR);
1342
	}
1343
	
1344
	/**
1345
	 * Return directory content or false on error
1346
	 *
1347
	 * @param  string   $hash   file hash
1348
	 * @return array|false
1349
	 * @author Dmitry (dio) Levashov
1350
	 **/
1351
	public function scandir($hash) {
1352
		if (($dir = $this->dir($hash)) == false) {
1353
			return false;
1354
		}
1355
		
1356
		return $dir['read']
1357
			? $this->getScandir($this->decode($hash))
1358
			: $this->setError(elFinder::ERROR_PERM_DENIED);
1359
	}
1360
1361
	/**
1362
	 * Return dir files names list
1363
	 * 
1364
	 * @param  string  $hash   file hash
1365
	 * @return array
1366
	 * @author Dmitry (dio) Levashov
1367
	 **/
1368
	public function ls($hash) {
1369 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...
1370
			return false;
1371
		}
1372
		
1373
		$list = array();
1374
		$path = $this->decode($hash);
1375
		
1376
		foreach ($this->getScandir($path) as $stat) {
1377
			if (empty($stat['hidden']) && $this->mimeAccepted($stat['mime'])) {
1378
				$list[] = $stat['name'];
1379
			}
1380
		}
1381
1382
		return $list;
1383
	}
1384
1385
	/**
1386
	 * Return subfolders for required folder or false on error
1387
	 *
1388
	 * @param  string   $hash  folder hash or empty string to get tree from root folder
1389
	 * @param  int      $deep  subdir deep
1390
	 * @param  string   $exclude  dir hash which subfolders must be exluded from result, required to not get stat twice on cwd subfolders
1391
	 * @return array|false
1392
	 * @author Dmitry (dio) Levashov
1393
	 **/
1394
	public function tree($hash='', $deep=0, $exclude='') {
1395
		$path = $hash ? $this->decode($hash) : $this->root;
1396
		
1397 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...
1398
			return false;
1399
		}
1400
		
1401
		$dirs = $this->gettree($path, $deep > 0 ? $deep -1 : $this->treeDeep-1, $exclude ? $this->decode($exclude) : null);
1402
		array_unshift($dirs, $dir);
1403
		return $dirs;
1404
	}
1405
	
1406
	/**
1407
	 * Return part of dirs tree from required dir up to root dir
1408
	 *
1409
	 * @param  string    $hash   directory hash
1410
	 * @param  bool|null $lineal only lineal parents
1411
	 * @return array
1412
	 * @author Dmitry (dio) Levashov
1413
	 **/
1414
	public function parents($hash, $lineal = false) {
1415
		if (($current = $this->dir($hash)) == false) {
1416
			return false;
1417
		}
1418
1419
		$path = $this->decode($hash);
1420
		$tree = array();
1421
		
1422
		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...
1423
			$path = $this->dirnameCE($path);
1424
			$stat = $this->stat($path);
1425
			if (!empty($stat['hidden']) || !$stat['read']) {
1426
				return false;
1427
			}
1428
			
1429
			array_unshift($tree, $stat);
1430
			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...
1431
				foreach ($this->gettree($path, 0) as $dir) {
1432
					if (!in_array($dir, $tree)) {
1433
						$tree[] = $dir;
1434
					}
1435
				}
1436
			}
1437
		}
1438
1439
		return $tree ? $tree : array($current);
1440
	}
1441
	
1442
	/**
1443
	 * Create thumbnail for required file and return its name of false on failed
1444
	 *
1445
	 * @return string|false
1446
	 * @author Dmitry (dio) Levashov
1447
	 **/
1448
	public function tmb($hash) {
1449
		$path = $this->decode($hash);
1450
		$stat = $this->stat($path);
1451
		
1452
		if (isset($stat['tmb'])) {
1453
			return $stat['tmb'] == "1" ? $this->createTmb($path, $stat) : $stat['tmb'];
1454
		}
1455
		return false;
1456
	}
1457
	
1458
	/**
1459
	 * Return file size / total directory size
1460
	 *
1461
	 * @param  string   file hash
1462
	 * @return int
1463
	 * @author Dmitry (dio) Levashov
1464
	 **/
1465
	public function size($hash) {
1466
		return $this->countSize($this->decode($hash));
1467
	}
1468
	
1469
	/**
1470
	 * Open file for reading and return file pointer
1471
	 *
1472
	 * @param  string   file hash
1473
	 * @return Resource
1474
	 * @author Dmitry (dio) Levashov
1475
	 **/
1476
	public function open($hash) {
1477 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...
1478
		|| $file['mime'] == 'directory') {
1479
			return false;
1480
		}
1481
		
1482
		return $this->fopenCE($this->decode($hash), 'rb');
1483
	}
1484
	
1485
	/**
1486
	 * Close file pointer
1487
	 *
1488
	 * @param  Resource  $fp   file pointer
1489
	 * @param  string    $hash file hash
1490
	 * @return void
1491
	 * @author Dmitry (dio) Levashov
1492
	 **/
1493
	public function close($fp, $hash) {
1494
		$this->fcloseCE($fp, $this->decode($hash));
1495
	}
1496
	
1497
	/**
1498
	 * Create directory and return dir info
1499
	 *
1500
	 * @param  string   $dsthash  destination directory hash
1501
	 * @param  string   $name directory name
1502
	 * @return array|false
1503
	 * @author Dmitry (dio) Levashov
1504
	 **/
1505
	public function mkdir($dsthash, $name) {
1506
		if ($this->commandDisabled('mkdir')) {
1507
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1508
		}
1509
		
1510
		if (!$this->nameAccepted($name)) {
1511
			return $this->setError(elFinder::ERROR_INVALID_NAME);
1512
		}
1513
		
1514
		if (($dir = $this->dir($dsthash)) == false) {
1515
			return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dsthash);
1516
		}
1517
		
1518
		$path = $this->decode($dsthash);
1519
		
1520
		if (!$dir['write'] || !$this->allowCreate($path, $name, true)) {
1521
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1522
		}
1523
		
1524
		$dst  = $this->joinPathCE($path, $name);
1525
		$stat = $this->stat($dst); 
1526
		if (!empty($stat)) { 
1527
			return $this->setError(elFinder::ERROR_EXISTS, $name);
1528
		}
1529
		$this->clearcache();
1530
		return ($path = $this->convEncOut($this->_mkdir($this->convEncIn($path), $this->convEncIn($name)))) ? $this->stat($path) : false;
1531
	}
1532
	
1533
	/**
1534
	 * Create empty file and return its info
1535
	 *
1536
	 * @param  string   $dst  destination directory
1537
	 * @param  string   $name file name
1538
	 * @return array|false
1539
	 * @author Dmitry (dio) Levashov
1540
	 **/
1541
	public function mkfile($dst, $name) {
1542
		if ($this->commandDisabled('mkfile')) {
1543
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1544
		}
1545
		
1546
		if (!$this->nameAccepted($name)) {
1547
			return $this->setError(elFinder::ERROR_INVALID_NAME);
1548
		}
1549
		
1550 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...
1551
			return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst);
1552
		}
1553
		
1554
		$path = $this->decode($dst);
1555
		
1556
		if (!$dir['write'] || !$this->allowCreate($path, $name, false)) {
1557
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1558
		}
1559
		
1560
		if ($this->stat($this->joinPathCE($path, $name))) {
1561
			return $this->setError(elFinder::ERROR_EXISTS, $name);
1562
		}
1563
		
1564
		$this->clearcache();
1565
		return ($path = $this->convEncOut($this->_mkfile($this->convEncIn($path), $this->convEncIn($name)))) ? $this->stat($path) : false;
1566
	}
1567
	
1568
	/**
1569
	 * Rename file and return file info
1570
	 *
1571
	 * @param  string  $hash  file hash
1572
	 * @param  string  $name  new file name
1573
	 * @return array|false
1574
	 * @author Dmitry (dio) Levashov
1575
	 **/
1576
	public function rename($hash, $name) {
1577
		if ($this->commandDisabled('rename')) {
1578
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1579
		}
1580
		
1581
		if (!$this->nameAccepted($name)) {
1582
			return $this->setError(elFinder::ERROR_INVALID_NAME, $name);
1583
		}
1584
		
1585
		$mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name);
1586 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...
1587
			return $this->setError(elFinder::ERROR_INVALID_NAME, $name);
1588
		}
1589
		
1590
		if (!($file = $this->file($hash))) {
1591
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1592
		}
1593
		
1594
		if ($name == $file['name']) {
1595
			return $file;
1596
		}
1597
		
1598
		if (!empty($file['locked'])) {
1599
			return $this->setError(elFinder::ERROR_LOCKED, $file['name']);
1600
		}
1601
		
1602
		$path = $this->decode($hash);
1603
		$dir  = $this->dirnameCE($path);
1604
		$stat = $this->stat($this->joinPathCE($dir, $name));
1605
		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...
1606
			return $this->setError(elFinder::ERROR_EXISTS, $name);
1607
		}
1608
		
1609 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...
1610
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1611
		}
1612
1613
		$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...
1614
1615
1616 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...
1617
			$this->clearcache();
1618
			return $this->stat($path);
1619
		}
1620
		return false;
1621
	}
1622
	
1623
	/**
1624
	 * Create file copy with suffix "copy number" and return its info
1625
	 *
1626
	 * @param  string   $hash    file hash
1627
	 * @param  string   $suffix  suffix to add to file name
1628
	 * @return array|false
1629
	 * @author Dmitry (dio) Levashov
1630
	 **/
1631
	public function duplicate($hash, $suffix='copy') {
1632
		if ($this->commandDisabled('duplicate')) {
1633
			return $this->setError(elFinder::ERROR_COPY, '#'.$hash, elFinder::ERROR_PERM_DENIED);
1634
		}
1635
		
1636
		if (($file = $this->file($hash)) == false) {
1637
			return $this->setError(elFinder::ERROR_COPY, elFinder::ERROR_FILE_NOT_FOUND);
1638
		}
1639
1640
		$path = $this->decode($hash);
1641
		$dir  = $this->dirnameCE($path);
1642
		$name = $this->uniqueName($dir, $this->basenameCE($path), ' '.$suffix.' ');
1643
1644 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...
1645
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1646
		}
1647
1648
		return ($path = $this->copy($path, $dir, $name)) == false
1649
			? false
1650
			: $this->stat($path);
0 ignored issues
show
Bug introduced by
It seems like $path defined by $this->copy($path, $dir, $name) on line 1648 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...
1651
	}
1652
	
1653
	/**
1654
	 * Save uploaded file. 
1655
	 * On success return array with new file stat and with removed file hash (if existed file was replaced)
1656
	 *
1657
	 * @param  Resource $fp      file pointer
1658
	 * @param  string   $dst     destination folder hash
1659
	 * @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...
1660
	 * @param  string   $tmpname file tmp name - required to detect mime type
1661
	 * @return array|false
1662
	 * @author Dmitry (dio) Levashov
1663
	 **/
1664
	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...
1665
		if ($this->commandDisabled('upload')) {
1666
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1667
		}
1668
		
1669 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...
1670
			return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst);
1671
		}
1672
1673
		if (!$dir['write']) {
1674
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1675
		}
1676
		
1677
		if (!$this->nameAccepted($name)) {
1678
			return $this->setError(elFinder::ERROR_INVALID_NAME);
1679
		}
1680
		
1681
		$mime = $this->mimetype($this->mimeDetect == 'internal' ? $name : $tmpname, $name);
1682
		$mimeByName = '';
1683
		if ($this->mimeDetect !== 'internal') {
1684
			$mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name);
1685
			if ($mime == 'unknown') {
1686
				$mime = $mimeByName;
1687
			}
1688
		}
1689
1690 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...
1691
			return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME);
1692
		}
1693
1694
		$tmpsize = sprintf('%u', filesize($tmpname));
1695
		if ($this->uploadMaxSize > 0 && $tmpsize > $this->uploadMaxSize) {
1696
			return $this->setError(elFinder::ERROR_UPLOAD_FILE_SIZE);
1697
		}
1698
1699
		$dstpath = $this->decode($dst);
1700
		$test    = $this->joinPathCE($dstpath, $name);
1701
		
1702
		$file = $this->stat($test);
1703
		$this->clearcache();
1704
		
1705
		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...
1706
			// check POST data `overwrite` for 3rd party uploader
1707
			$overwrite = isset($_POST['overwrite'])? (bool)$_POST['overwrite'] : $this->options['uploadOverwrite'];
1708
			if ($overwrite) {
1709 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...
1710
					return $this->setError(elFinder::ERROR_PERM_DENIED);
1711
				} elseif ($file['mime'] == 'directory') {
1712
					return $this->setError(elFinder::ERROR_NOT_REPLACE, $name);
1713
				} 
1714
				$this->remove($test);
1715
			} else {
1716
				$name = $this->uniqueName($dstpath, $name, '-', false);
1717
			}
1718
		}
1719
		
1720
		$stat = array(
1721
			'mime'   => $mime, 
1722
			'width'  => 0, 
1723
			'height' => 0, 
1724
			'size'   => $tmpsize);
1725
		
1726
		// $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...
1727
		if (strpos($mime, 'image') === 0 && ($s = getimagesize($tmpname))) {
1728
			$stat['width'] = $s[0];
1729
			$stat['height'] = $s[1];
1730
		}
1731
		// $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...
1732
		if (($path = $this->saveCE($fp, $dstpath, $name, $stat)) == false) {
1733
			return false;
1734
		}
1735
		
1736
		
1737
1738
		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 1732 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...
1739
	}
1740
	
1741
	/**
1742
	 * Paste files
1743
	 *
1744
	 * @param  Object  $volume  source volume
1745
	 * @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...
1746
	 * @param  string  $dst     destination dir hash
1747
	 * @param  bool    $rmSrc   remove source after copy?
1748
	 * @return array|false
1749
	 * @author Dmitry (dio) Levashov
1750
	 **/
1751
	public function paste($volume, $src, $dst, $rmSrc = false) {
1752
		$err = $rmSrc ? elFinder::ERROR_MOVE : elFinder::ERROR_COPY;
1753
		
1754
		if ($this->commandDisabled('paste')) {
1755
			return $this->setError($err, '#'.$src, elFinder::ERROR_PERM_DENIED);
1756
		}
1757
1758
		if (($file = $volume->file($src, $rmSrc)) == false) {
1759
			return $this->setError($err, '#'.$src, elFinder::ERROR_FILE_NOT_FOUND);
1760
		}
1761
1762
		$name = $file['name'];
1763
		$errpath = $volume->path($file['hash']);
1764
		
1765 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...
1766
			return $this->setError($err, $errpath, elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst);
1767
		}
1768
		
1769
		if (!$dir['write'] || !$file['read']) {
1770
			return $this->setError($err, $errpath, elFinder::ERROR_PERM_DENIED);
1771
		}
1772
1773
		$destination = $this->decode($dst);
1774
1775
		if (($test = $volume->closest($src, $rmSrc ? 'locked' : 'read', $rmSrc))) {
1776
			return $rmSrc
1777
				? $this->setError($err, $errpath, elFinder::ERROR_LOCKED, $volume->path($test))
1778
				: $this->setError($err, $errpath, !empty($file['thash'])? elFinder::ERROR_PERM_DENIED : elFinder::ERROR_MKOUTLINK);
1779
		}
1780
1781
		$test = $this->joinPathCE($destination, $name);
1782
		$stat = $this->stat($test);
1783
		$this->clearcache();
1784
		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...
1785
			if ($this->options['copyOverwrite']) {
1786
				// do not replace file with dir or dir with file
1787
				if (!$this->isSameType($file['mime'], $stat['mime'])) {
1788
					return $this->setError(elFinder::ERROR_NOT_REPLACE, $this->path($stat['hash']));
1789
				}
1790
				// existed file is not writable
1791
				if (!$stat['write']) {
1792
					return $this->setError($err, $errpath, elFinder::ERROR_PERM_DENIED);
1793
				}
1794
				// existed file locked or has locked child
1795
				if (($locked = $this->closestByAttr($test, 'locked', true))) {
1796
					$stat = $this->stat($locked);
1797
					return $this->setError(elFinder::ERROR_LOCKED, $this->path($stat['hash']));
1798
				}
1799
				// target is entity file of alias
1800
				if ($volume == $this && ($test == @$file['target'] || $test == $this->decode($src))) {
1801
					return $this->setError(elFinder::ERROR_REPLACE, $errpath);
1802
				}
1803
				// remove existed file
1804
				if (!$this->remove($test)) {
1805
					return $this->setError(elFinder::ERROR_REPLACE, $this->path($stat['hash']));
1806
				}
1807
			} else {
1808
				$name = $this->uniqueName($destination, $name, ' ', false);
1809
			}
1810
		}
1811
		
1812
		// copy/move inside current volume
1813
		if ($volume == $this) {
1814
			$source = $this->decode($src);
1815
			// do not copy into itself
1816
			if ($this->inpathCE($destination, $source)) {
1817
				return $this->setError(elFinder::ERROR_COPY_INTO_ITSELF, $errpath);
1818
			}
1819
			$method = $rmSrc ? 'move' : 'copy';
1820
			return ($path = $this->$method($source, $destination, $name)) ? $this->stat($path) : false;
1821
		}
1822
		
1823
		// copy/move from another volume
1824
		if (!$this->options['copyTo'] || !$volume->copyFromAllowed()) {
1825
			return $this->setError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_PERM_DENIED);
1826
		}
1827
		
1828
		if (($path = $this->copyFrom($volume, $src, $destination, $name)) == false) {
1829
			return false;
1830
		}
1831
		
1832
		if ($rmSrc) {
1833
			if (!$volume->rm($src)) {
1834
				return $this->setError(elFinder::ERROR_MOVE, $errpath, elFinder::ERROR_RM_SRC);
1835
			}
1836
		}
1837
		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 1828 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...
1838
	}
1839
	
1840
	/**
1841
	 * Return file contents
1842
	 *
1843
	 * @param  string  $hash  file hash
1844
	 * @return string|false
1845
	 * @author Dmitry (dio) Levashov
1846
	 **/
1847
	public function getContents($hash) {
1848
		$file = $this->file($hash);
1849
		
1850
		if (!$file) {
1851
			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...
1852
		}
1853
		
1854
		if ($file['mime'] == 'directory') {
1855
			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...
1856
		}
1857
		
1858
		if (!$file['read']) {
1859
			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...
1860
		}
1861
		
1862
		return $this->convEncOut($this->_getContents($this->convEncIn($this->decode($hash))));
1863
	}
1864
	
1865
	/**
1866
	 * Put content in text file and return file info.
1867
	 *
1868
	 * @param  string  $hash     file hash
1869
	 * @param  string  $content  new file content
1870
	 * @return array
1871
	 * @author Dmitry (dio) Levashov
1872
	 **/
1873
	public function putContents($hash, $content) {
1874
		if ($this->commandDisabled('edit')) {
1875
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1876
		}
1877
		
1878
		$path = $this->decode($hash);
1879
		
1880
		if (!($file = $this->file($hash))) {
1881
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1882
		}
1883
		
1884
		if (!$file['write']) {
1885
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1886
		}
1887
		
1888
		// check MIME
1889
		$name = $this->basenameCE($path);
1890
		$mime = '';
1891
		$mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name);
1892
		if ($this->mimeDetect !== 'internal') {
1893
			if ($tp = tmpfile()) {
1894
				fwrite($tp, $content);
1895
				$info = stream_get_meta_data($tp);
1896
				$filepath = $info['uri'];
1897
				$mime = $this->mimetype($filepath, $name);
1898
				fclose($tp);
1899
			}
1900
		}
1901 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...
1902
			return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME);
1903
		}
1904
		
1905
		$this->clearcache();
1906
		return $this->convEncOut($this->_filePutContents($this->convEncIn($path), $content)) ? $this->stat($path) : false;
1907
	}
1908
	
1909
	/**
1910
	 * Extract files from archive
1911
	 *
1912
	 * @param  string  $hash  archive hash
1913
	 * @return array|bool
1914
	 * @author Dmitry (dio) Levashov, 
1915
	 * @author Alexey Sukhotin
1916
	 **/
1917
	public function extract($hash, $makedir = null) {
1918
		if ($this->commandDisabled('extract')) {
1919
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1920
		}
1921
		
1922
		if (($file = $this->file($hash)) == false) {
1923
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1924
		}
1925
		
1926
		$archiver = isset($this->archivers['extract'][$file['mime']])
1927
			? $this->archivers['extract'][$file['mime']]
1928
			: false;
1929
			
1930
		if (!$archiver) {
1931
			return $this->setError(elFinder::ERROR_NOT_ARCHIVE);
1932
		}
1933
		
1934
		$path   = $this->decode($hash);
1935
		$parent = $this->stat($this->dirnameCE($path));
1936
1937
		if (!$file['read'] || !$parent['write']) {
1938
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1939
		}
1940
		$this->clearcache();
1941
		$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...
1942
		
1943
		if ($path = $this->convEncOut($this->_extract($this->convEncIn($path), $archiver))) {
1944
			if (is_array($path)) {
1945
				foreach ($path as $_k => $_p) {
1946
					$path[$_k] = $this->stat($_p);
1947
				}
1948
			} else {
1949
				$path = $this->stat($path);
1950
			}
1951
			return $path;
1952
		} else {
1953
			return false;
1954
		}
1955
	}
1956
1957
	/**
1958
	 * Add files to archive
1959
	 *
1960
	 * @return void
1961
	 **/
1962
	public function archive($hashes, $mime, $name = '') {
1963
		if ($this->commandDisabled('archive')) {
1964
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1965
		}
1966
1967
		$archiver = isset($this->archivers['create'][$mime])
1968
			? $this->archivers['create'][$mime]
1969
			: false;
1970
			
1971
		if (!$archiver) {
1972
			return $this->setError(elFinder::ERROR_ARCHIVE_TYPE);
1973
		}
1974
		
1975
		$files = array();
1976
		
1977
		foreach ($hashes as $hash) {
1978
			if (($file = $this->file($hash)) == false) {
1979
				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...
1980
			}
1981
			if (!$file['read']) {
1982
				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...
1983
			}
1984
			$path = $this->decode($hash);
1985
			if (!isset($dir)) {
1986
				$dir = $this->dirnameCE($path);
1987
				$stat = $this->stat($dir);
1988
				if (!$stat['write']) {
1989
					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...
1990
				}
1991
			}
1992
			
1993
			$files[] = $this->basenameCE($path);
1994
		}
1995
		
1996
		if ($name === '') {
1997
			$name = count($files) == 1 ? $files[0] : 'Archive';
1998
		} else {
1999
			$name = str_replace(array('/', '\\'), '_', preg_replace('/\.' . preg_quote($archiver['ext'], '/') . '$/i', '', $name));
2000
		}
2001
		$name .='.' . $archiver['ext'];
2002
		$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...
2003
		$this->clearcache();
2004
		return ($path = $this->convEncOut($this->_archive($this->convEncIn($dir), $this->convEncIn($files), $this->convEncIn($name), $archiver))) ? $this->stat($path) : false;
2005
	}
2006
	
2007
	/**
2008
	 * Resize image
2009
	 *
2010
	 * @param  string   $hash    image file
2011
	 * @param  int      $width   new width
2012
	 * @param  int      $height  new height
2013
	 * @param  int      $x       X start poistion for crop
2014
	 * @param  int      $y       Y start poistion for crop
2015
	 * @param  string   $mode    action how to mainpulate image
2016
	 * @param  string   $bg      background color
2017
	 * @param  int      $degree  rotete degree
2018
	 * @param  int      $jpgQuality  JEPG quality (1-100)
2019
	 * @return array|false
2020
	 * @author Dmitry (dio) Levashov
2021
	 * @author Alexey Sukhotin
2022
	 * @author nao-pon
2023
	 * @author Troex Nevelin
2024
	 **/
2025
	public function resize($hash, $width, $height, $x, $y, $mode = 'resize', $bg = '', $degree = 0, $jpgQuality = null) {
2026
		if ($this->commandDisabled('resize')) {
2027
			return $this->setError(elFinder::ERROR_PERM_DENIED);
2028
		}
2029
		
2030
		if (($file = $this->file($hash)) == false) {
2031
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
2032
		}
2033
		
2034
		if (!$file['write'] || !$file['read']) {
2035
			return $this->setError(elFinder::ERROR_PERM_DENIED);
2036
		}
2037
		
2038
		$path = $this->decode($hash);
2039
		
2040
		$work_path = $this->getWorkFile($this->encoding? $this->convEncIn($path, true) : $path);
2041
2042
		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...
2043
			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...
2044
				@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...
2045
			}
2046
			return $this->setError(elFinder::ERROR_PERM_DENIED);
2047
		}
2048
2049
		if ($this->imgLib != 'imagick') {
2050
			if (elFinder::isAnimationGif($work_path)) {
2051
				return $this->setError(elFinder::ERROR_UNSUPPORT_TYPE);
2052
			}
2053
		}
2054
2055
		switch($mode) {
2056
			
2057 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...
2058
				$result = $this->imgResize($work_path, $width, $height, true, true, null, $jpgQuality);
2059
				break;
2060
2061
			case 'crop':
2062
				$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...
2063
				break;
2064
2065
			case 'fitsquare':
2066
				$result = $this->imgSquareFit($work_path, $width, $height, 'center', 'middle', ($bg ? $bg : $this->options['tmbBgColor']), null, $jpgQuality);
2067
				break;
2068
2069
			case 'rotate':
2070
				$result = $this->imgRotate($work_path, $degree, ($bg ? $bg : $this->options['tmbBgColor']), null, $jpgQuality);
2071
				break;
2072
2073 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...
2074
				$result = $this->imgResize($work_path, $width, $height, false, true, null, $jpgQuality);
2075
				break;
2076
		}
2077
		
2078
		$ret = false;
2079
		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...
2080
			$stat = $this->stat($path);
2081
			clearstatcache();
2082
			$fstat = stat($work_path);
2083
			$stat['size'] = $fstat['size'];
2084
			$stat['ts'] = $fstat['mtime'];
2085
			if ($imgsize = @getimagesize($work_path)) {
2086
				$stat['width'] = $imgsize[0];
2087
				$stat['height'] = $imgsize[1];
2088
				$stat['mime'] = $imgsize['mime'];
2089
			}
2090
			if ($path !== $work_path) {
2091
				if ($fp = @fopen($work_path, 'rb')) {
2092
					$ret = $this->saveCE($fp, $this->dirnameCE($path), $this->basenameCE($path), $stat);
2093
					@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...
2094
				}
2095
			} else {
2096
				$ret = true;
2097
			}
2098
			if ($ret) {
2099
				$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...
2100
				$this->clearcache();
2101
				$ret = $this->stat($path);
2102
				$ret['width'] = $stat['width'];
2103
				$ret['height'] = $stat['height'];
2104
			}
2105
		}
2106
		if ($path !== $work_path) {
2107
			is_file($work_path) && @unlink($work_path);
2108
		}
2109
		
2110
		return $ret;
2111
	}
2112
	
2113
	/**
2114
	 * Remove file/dir
2115
	 *
2116
	 * @param  string  $hash  file hash
2117
	 * @return bool
2118
	 * @author Dmitry (dio) Levashov
2119
	 **/
2120
	public function rm($hash) {
2121
		return $this->commandDisabled('rm')
2122
			? $this->setError(elFinder::ERROR_PERM_DENIED)
2123
			: $this->remove($this->decode($hash));
2124
	}
2125
	
2126
	/**
2127
	 * Search files
2128
	 *
2129
	 * @param  string  $q  search string
2130
	 * @param  array   $mimes
2131
	 * @return array
2132
	 * @author Dmitry (dio) Levashov
2133
	 **/
2134
	public function search($q, $mimes, $hash = null) {
2135
		$dir = null;
2136
		if ($hash) {
2137
			$dir = $this->decode($hash);
2138
			$stat = $this->stat($dir);
2139
			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...
2140
				$q = '';
2141
			}
2142
		}
2143
		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...
2144
			$mimes = array_intersect($mimes, $this->onlyMimes);
2145
			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...
2146
				$q = '';
2147
			}
2148
		}
2149
		$this->searchStart = time();
2150
		return ($q === '' || $this->commandDisabled('search'))
2151
			? array()
2152
			: $this->doSearch(is_null($dir)? $this->root : $dir, $q, $mimes);
2153
	}
2154
	
2155
	/**
2156
	 * Return image dimensions
2157
	 *
2158
	 * @param  string  $hash  file hash
2159
	 * @return array
2160
	 * @author Dmitry (dio) Levashov
2161
	 **/
2162
	public function dimensions($hash) {
2163
		if (($file = $this->file($hash)) == false) {
2164
			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...
2165
		}
2166
		
2167
		return $this->convEncOut($this->_dimensions($this->convEncIn($this->decode($hash)), $file['mime']));
2168
	}
2169
	
2170
	/**
2171
	 * Return content URL (for netmout volume driver)
2172
	 * If file.url == 1 requests from JavaScript client with XHR
2173
	 * 
2174
	 * @param string $hash  file hash
2175
	 * @param array $options  options array
2176
	 * @return boolean|string
2177
	 * @author Naoki Sawada
2178
	 */
2179
	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...
2180 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...
2181
			return false;
2182
		}
2183
		return $file['url'];
2184
	}
2185
	
2186
	/**
2187
	 * Return temp path
2188
	 * 
2189
	 * @return string
2190
	 * @author Naoki Sawada
2191
	 */
2192
	public function getTempPath() {
2193
		$tempPath = null;
2194
		if (isset($this->tmpPath) && $this->tmpPath && is_writable($this->tmpPath)) {
2195
			$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...
2196
		} else if (isset($this->tmp) && $this->tmp && is_writable($this->tmp)) {
2197
			$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...
2198
		} else if (function_exists('sys_get_temp_dir')) {
2199
			$tempPath = sys_get_temp_dir();
2200
		} else if (isset($this->tmbPath) && $this->tmbPath && is_writable($this->tmbPath)) {
2201
			$tempPath = $this->tmbPath;
2202
		}
2203
		if ($tempPath && DIRECTORY_SEPARATOR !== '/') {
2204
			$tempPath = str_replace('/', DIRECTORY_SEPARATOR, $tempPath);
2205
		}
2206
		return $tempPath;
2207
	}
2208
	
2209
	/**
2210
	 * (Make &) Get upload taget dirctory hash
2211
	 * 
2212
	 * @param string $baseTargetHash
2213
	 * @param string $path
2214
	 * @param array  $result
2215
	 * @return boolean|string
2216
	 * @author Naoki Sawada
2217
	 */
2218
	public function getUploadTaget($baseTargetHash, $path, & $result) {
2219
		$base = $this->decode($baseTargetHash);
2220
		$targetHash = $baseTargetHash;
2221
		$path = ltrim($path, $this->separator);
2222
		$dirs = explode($this->separator, $path);
2223
		array_pop($dirs);
2224
		foreach($dirs as $dir) {
2225
			$targetPath = $this->joinPathCE($base, $dir);
2226
			if (! $_realpath = $this->realpath($this->encode($targetPath))) {
2227
				if ($stat = $this->mkdir($targetHash, $dir)) {
2228
					$result['added'][] = $stat;
2229
					$targetHash = $stat['hash'];
2230
					$base = $this->decode($targetHash);
2231
				} else {
2232
					return false;
2233
				}
2234
			} else {
2235
				$targetHash = $this->encode($_realpath);
2236
				if ($this->dir($targetHash)) {
2237
					$base = $this->decode($targetHash);
2238
				} else {
2239
					return false;
2240
				}
2241
			}
2242
		}
2243
		return $targetHash;
2244
	}
2245
	
2246
	/**
2247
	 * Return this uploadMaxSize value
2248
	 * 
2249
	 * @return integer
2250
	 * @author Naoki Sawada
2251
	 */
2252
	public function getUploadMaxSize() {
2253
		return $this->uploadMaxSize;
2254
	}
2255
	
2256
	/**
2257
	 * Save error message
2258
	 *
2259
	 * @param  array  error 
2260
	 * @return false
2261
	 * @author Dmitry(dio) Levashov
2262
	 **/
2263
	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...
2264
		
2265
		$this->error = array();
2266
		
2267
		foreach (func_get_args() as $err) {
2268
			if (is_array($err)) {
2269
				$this->error = array_merge($this->error, $err);
2270
			} else {
2271
				$this->error[] = $err;
2272
			}
2273
		}
2274
		
2275
		// $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...
2276
		return false;
2277
	}
2278
	
2279
	/*********************************************************************/
2280
	/*                               FS API                              */
2281
	/*********************************************************************/
2282
	
2283
	/***************** server encoding support *******************/
2284
	
2285
	/**
2286
	 * Return parent directory path (with convert encording)
2287
	 *
2288
	 * @param  string  $path  file path
2289
	 * @return string
2290
	 * @author Naoki Sawada
2291
	 **/
2292
	protected function dirnameCE($path) {
2293
		return (!$this->encoding)? $this->_dirname($path) :	$this->convEncOut($this->_dirname($this->convEncIn($path)));
2294
	}
2295
	
2296
	/**
2297
	 * Return file name (with convert encording)
2298
	 *
2299
	 * @param  string  $path  file path
2300
	 * @return string
2301
	 * @author Naoki Sawada
2302
	 **/
2303
	protected function basenameCE($path) {
2304
		return (!$this->encoding)? $this->_basename($path) : $this->convEncOut($this->_basename($this->convEncIn($path)));
2305
	}
2306
	
2307
	/**
2308
	 * Join dir name and file name and return full path. (with convert encording)
2309
	 * Some drivers (db) use int as path - so we give to concat path to driver itself
2310
	 *
2311
	 * @param  string  $dir   dir path
2312
	 * @param  string  $name  file name
2313
	 * @return string
2314
	 * @author Naoki Sawada
2315
	 **/
2316
	protected function joinPathCE($dir, $name) {
2317
		return (!$this->encoding)? $this->_joinPath($dir, $name) : $this->convEncOut($this->_joinPath($this->convEncIn($dir), $this->convEncIn($name)));
2318
	}
2319
	
2320
	/**
2321
	 * Return normalized path (with convert encording)
2322
	 *
2323
	 * @param  string  $path  file path
2324
	 * @return string
2325
	 * @author Naoki Sawada
2326
	 **/
2327
	protected function normpathCE($path) {
2328
		return (!$this->encoding)? $this->_normpath($path) : $this->convEncOut($this->_normpath($this->convEncIn($path)));
2329
	}
2330
	
2331
	/**
2332
	 * Return file path related to root dir (with convert encording)
2333
	 *
2334
	 * @param  string  $path  file path
2335
	 * @return string
2336
	 * @author Naoki Sawada
2337
	 **/
2338
	protected function relpathCE($path) {
2339
		return (!$this->encoding)? $this->_relpath($path) : $this->convEncOut($this->_relpath($this->convEncIn($path)));
2340
	}
2341
	
2342
	/**
2343
	 * Convert path related to root dir into real path (with convert encording)
2344
	 *
2345
	 * @param  string  $path  rel file path
2346
	 * @return string
2347
	 * @author Naoki Sawada
2348
	 **/
2349
	protected function abspathCE($path) {
2350
		return (!$this->encoding)? $this->_abspath($path): $this->convEncOut($this->_abspath($this->convEncIn($path)));
2351
	}
2352
	
2353
	/**
2354
	 * Return true if $path is children of $parent (with convert encording)
2355
	 *
2356
	 * @param  string  $path    path to check
2357
	 * @param  string  $parent  parent path
2358
	 * @return bool
2359
	 * @author Naoki Sawada
2360
	 **/
2361
	protected function inpathCE($path, $parent) {
2362
		return (!$this->encoding)? $this->_inpath($path, $parent) : $this->convEncOut($this->_inpath($this->convEncIn($path), $this->convEncIn($parent)));
2363
	}
2364
	
2365
	/**
2366
	 * Open file and return file pointer (with convert encording)
2367
	 *
2368
	 * @param  string  $path  file path
2369
	 * @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...
2370
	 * @return resource|false
2371
	 * @author Naoki Sawada
2372
	 **/
2373
	protected function fopenCE($path, $mode='rb') {
2374
		return (!$this->encoding)? $this->_fopen($path, $mode) : $this->convEncOut($this->_fopen($this->convEncIn($path), $mode));
2375
	}
2376
	
2377
	/**
2378
	 * Close opened file (with convert encording)
2379
	 * 
2380
	 * @param  resource  $fp    file pointer
2381
	 * @param  string    $path  file path
2382
	 * @return bool
2383
	 * @author Naoki Sawada
2384
	 **/
2385
	protected function fcloseCE($fp, $path='') {
2386
		return (!$this->encoding)? $this->_fclose($fp, $path) : $this->convEncOut($this->_fclose($fp, $this->convEncIn($path)));
2387
	}
2388
	
2389
	/**
2390
	 * Create new file and write into it from file pointer. (with convert encording)
2391
	 * Return new file path or false on error.
2392
	 *
2393
	 * @param  resource  $fp   file pointer
2394
	 * @param  string    $dir  target dir path
2395
	 * @param  string    $name file name
2396
	 * @param  array     $stat file stat (required by some virtual fs)
2397
	 * @return bool|string
2398
	 * @author Naoki Sawada
2399
	 **/
2400
	protected function saveCE($fp, $dir, $name, $stat) {
2401
		return (!$this->encoding)? $this->_save($fp, $dir, $name, $stat) : $this->convEncOut($this->_save($fp, $this->convEncIn($dir), $this->convEncIn($name), $this->convEncIn($stat)));
2402
	}
2403
	
2404
	/**
2405
	 * Return true if path is dir and has at least one childs directory (with convert encording)
2406
	 *
2407
	 * @param  string  $path  dir path
2408
	 * @return bool
2409
	 * @author Naoki Sawada
2410
	 **/
2411
	protected function subdirsCE($path) {
2412
		if (!isset($this->subdirsCache[$path])) {
2413
			$this->subdirsCache[$path] = (!$this->encoding)? $this->_subdirs($path) : $this->convEncOut($this->_subdirs($this->convEncIn($path)));
2414
		}
2415
		return $this->subdirsCache[$path];
2416
	}
2417
	
2418
	/**
2419
	 * Return files list in directory (with convert encording)
2420
	 *
2421
	 * @param  string  $path  dir path
2422
	 * @return array
2423
	 * @author Naoki Sawada
2424
	 **/
2425
	protected function scandirCE($path) {
2426
		return (!$this->encoding)? $this->_scandir($path) : $this->convEncOut($this->_scandir($this->convEncIn($path)));
2427
	}
2428
	
2429
	/**
2430
	 * Create symlink (with convert encording)
2431
	 *
2432
	 * @param  string  $source     file to link to
2433
	 * @param  string  $targetDir  folder to create link in
2434
	 * @param  string  $name       symlink name
2435
	 * @return bool
2436
	 * @author Naoki Sawada
2437
	 **/
2438
	protected function symlinkCE($source, $targetDir, $name) {
2439
		return (!$this->encoding)? $this->_symlink($source, $targetDir, $name) : $this->convEncOut($this->_symlink($this->convEncIn($source), $this->convEncIn($targetDir), $this->convEncIn($name)));
2440
	}
2441
	
2442
	/***************** paths *******************/
2443
	
2444
	/**
2445
	 * Encode path into hash
2446
	 *
2447
	 * @param  string  file path
2448
	 * @return string
2449
	 * @author Dmitry (dio) Levashov
2450
	 * @author Troex Nevelin
2451
	 **/
2452
	protected function encode($path) {
2453
		if ($path !== '') {
2454
2455
			// cut ROOT from $path for security reason, even if hacker decodes the path he will not know the root
2456
			$p = $this->relpathCE($path);
2457
			// if reqesting root dir $path will be empty, then assign '/' as we cannot leave it blank for crypt
2458
			if ($p === '')	{
2459
				$p = DIRECTORY_SEPARATOR;
2460
			}
2461
2462
			// TODO crypt path and return hash
2463
			$hash = $this->crypt($p);
2464
			// hash is used as id in HTML that means it must contain vaild chars
2465
			// make base64 html safe and append prefix in begining
2466
			$hash = strtr(base64_encode($hash), '+/=', '-_.');
2467
			// remove dots '.' at the end, before it was '=' in base64
2468
			$hash = rtrim($hash, '.'); 
2469
			// append volume id to make hash unique
2470
			return $this->id.$hash;
2471
		}
2472
	}
2473
	
2474
	/**
2475
	 * Decode path from hash
2476
	 *
2477
	 * @param  string  file hash
2478
	 * @return string
2479
	 * @author Dmitry (dio) Levashov
2480
	 * @author Troex Nevelin
2481
	 **/
2482
	protected function decode($hash) {
2483
		if (strpos($hash, $this->id) === 0) {
2484
			// cut volume id after it was prepended in encode
2485
			$h = substr($hash, strlen($this->id));
2486
			// replace HTML safe base64 to normal
2487
			$h = base64_decode(strtr($h, '-_.', '+/='));
2488
			// TODO uncrypt hash and return path
2489
			$path = $this->uncrypt($h); 
2490
			// append ROOT to path after it was cut in encode
2491
			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...
2492
		}
2493
	}
2494
	
2495
	/**
2496
	 * Return crypted path 
2497
	 * Not implemented
2498
	 *
2499
	 * @param  string  path
2500
	 * @return mixed
2501
	 * @author Dmitry (dio) Levashov
2502
	 **/
2503
	protected function crypt($path) {
2504
		return $path;
2505
	}
2506
	
2507
	/**
2508
	 * Return uncrypted path 
2509
	 * Not implemented
2510
	 *
2511
	 * @param  mixed  hash
2512
	 * @return mixed
2513
	 * @author Dmitry (dio) Levashov
2514
	 **/
2515
	protected function uncrypt($hash) {
2516
		return $hash;
2517
	}
2518
	
2519
	/**
2520
	 * Validate file name based on $this->options['acceptedName'] regexp or function
2521
	 *
2522
	 * @param  string  $name  file name
2523
	 * @return bool
2524
	 * @author Dmitry (dio) Levashov
2525
	 **/
2526
	protected function nameAccepted($name) {
2527
		if (!json_encode($name)) {
2528
			return false;
2529
		}
2530
		if ($this->nameValidator) {
2531
			if (is_callable($this->nameValidator)) {
2532
				$res = call_user_func($this->nameValidator, $name);
2533
				return $res;
2534
			}
2535
			if (preg_match($this->nameValidator, '') !== false) {
2536
				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...
2537
			}
2538
		}
2539
		return true;
2540
	}
2541
	
2542
	/**
2543
	 * Return new unique name based on file name and suffix
2544
	 *
2545
	 * @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...
2546
	 * @param  string  $suffix  suffix append to name
2547
	 * @return string
2548
	 * @author Dmitry (dio) Levashov
2549
	 **/
2550
	public function uniqueName($dir, $name, $suffix = ' copy', $checkNum = true, $start = 1) {
2551
		$ext  = '';
2552
2553
		if (preg_match('/\.((tar\.(gz|bz|bz2|z|lzo))|cpio\.gz|ps\.gz|xcf\.(gz|bz2)|[a-z0-9]{1,4})$/i', $name, $m)) {
2554
			$ext  = '.'.$m[1];
2555
			$name = substr($name, 0,  strlen($name)-strlen($m[0]));
2556
		} 
2557
		
2558
		if ($checkNum && preg_match('/('.preg_quote($suffix, '/').')(\d*)$/i', $name, $m)) {
2559
			$i    = (int)$m[2];
2560
			$name = substr($name, 0, strlen($name)-strlen($m[2]));
2561
		} else {
2562
			$i     = $start;
2563
			$name .= $suffix;
2564
		}
2565
		$max = $i+100000;
2566
2567
		while ($i <= $max) {
2568
			$n = $name.($i > 0 ? $i : '').$ext;
2569
2570
			if (!$this->stat($this->joinPathCE($dir, $n))) {
2571
				$this->clearcache();
2572
				return $n;
2573
			}
2574
			$i++;
2575
		}
2576
		return $name.md5($dir).$ext;
2577
	}
2578
	
2579
	/**
2580
	 * Converts character encoding from UTF-8 to server's one
2581
	 * 
2582
	 * @param  mixed  $var           target string or array var
2583
	 * @param  bool   $restoreLocale do retore global locale, default is false
2584
	 * @param  string $unknown       replaces character for unknown
2585
	 * @return mixed
2586
	 * @author Naoki Sawada
2587
	 */
2588
	public function convEncIn($var = null, $restoreLocale = false, $unknown = '_') {
2589
		return (!$this->encoding)? $var : $this->convEnc($var, 'UTF-8', $this->encoding, $this->options['locale'], $restoreLocale, $unknown);
2590
	}
2591
	
2592
	/**
2593
	 * Converts character encoding from server's one to UTF-8
2594
	 * 
2595
	 * @param  mixed  $var           target string or array var
2596
	 * @param  bool   $restoreLocale do retore global locale, default is true
2597
	 * @param  string $unknown       replaces character for unknown
2598
	 * @return mixed
2599
	 * @author Naoki Sawada
2600
	 */
2601
	public function convEncOut($var = null, $restoreLocale = true, $unknown = '_') {
2602
		return (!$this->encoding)? $var : $this->convEnc($var, $this->encoding, 'UTF-8', $this->options['locale'], $restoreLocale, $unknown);
2603
	}
2604
	
2605
	/**
2606
	 * Converts character encoding (base function)
2607
	 * 
2608
	 * @param  mixed  $var     target string or array var
2609
	 * @param  string $from    from character encoding
2610
	 * @param  string $to      to character encoding
2611
	 * @param  string $locale  local locale
2612
	 * @param  string $unknown replaces character for unknown
2613
	 * @return mixed
2614
	 */
2615
	protected function convEnc($var, $from, $to, $locale, $restoreLocale, $unknown = '_') {
2616
		if (strtoupper($from) !== strtoupper($to)) {
2617
			if ($locale) {
2618
				@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...
2619
			}
2620
			if (is_array($var)) {
2621
				$_ret = array();
2622
				foreach($var as $_k => $_v) {
2623
					$_ret[$_k] = $this->convEnc($_v, $from, $to, '', false, $unknown = '_');
2624
				}
2625
				$var = $_ret;
2626
			} else {
2627
				$_var = false;
2628
				if (is_string($var)) {
2629
					$_var = $var;
2630
					if (false !== ($_var = @iconv($from, $to.'//TRANSLIT', $_var))) {
2631
						$_var = str_replace('?', $unknown, $_var);
2632
					}
2633
				}
2634
				if  ($_var !== false) {
2635
					$var = $_var;
2636
				}
2637
			}
2638
			if ($restoreLocale) {
2639
				setlocale(LC_ALL, elFinder::$locale);
2640
			}
2641
		}
2642
		return $var;
2643
	}
2644
	
2645
	/*********************** util mainly for inheritance class *********************/
2646
	
2647
	/**
2648
	 * Get temporary filename. Tempfile will be removed when after script execution finishes or exit() is called.
2649
	 * When needing the unique file to a path, give $path to parameter.
2650
	 * 
2651
	 * @param  string       $path for get unique file to a path
2652
	 * @return string|false
2653
	 * @author Naoki Sawada
2654
	 */
2655
	protected function getTempFile($path = '') {
2656
		static $cache = array();
2657
		static $rmfunc;
2658
		
2659
		$key = '';
2660
		if ($path !== '') {
2661
			$key = $this->id . '#' . $path;
2662
			if (isset($cache[$key])) {
2663
				return $cache[$key];
2664
			}
2665
		}
2666
		
2667
		if ($tmpdir = $this->getTempPath()) {
2668
			if (!$rmfunc) {
2669
				$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...
2670
			}
2671
			$name = tempnam($tmpdir, 'ELF');
2672
			if ($key) {
2673
				$cache[$key] = $name;
2674
			}
2675
			register_shutdown_function($rmfunc, $name);
2676
			return $name;
2677
		}
2678
		
2679
		return false;
2680
	}
2681
	
2682
	/**
2683
	 * File path of local server side work file path
2684
	 * 
2685
	 * @param  string $path path need convert encoding to server encoding
2686
	 * @return string
2687
	 * @author Naoki Sawada
2688
	 */
2689
	protected function getWorkFile($path) {
2690
		if ($work = $this->getTempFile()) {
2691
			if ($wfp = fopen($work, 'wb')) {
2692
				if ($fp = $this->_fopen($path)) {
2693
					while(!feof($fp)) {
2694
						fwrite($wfp, fread($fp, 8192));
2695
					}
2696
					$this->_fclose($fp, $path);
2697
					fclose($wfp);
2698
					return $work;
2699
				}
2700
			}
2701
		}
2702
		return false;
2703
	}
2704
	
2705
	/**
2706
	 * Get image size array with `dimensions`
2707
	 *
2708
	 * @param string $path path need convert encoding to server encoding
2709
	 * @param string $mime file mime type
2710
	 * @return array|false
2711
	 */
2712
	public function getImageSize($path, $mime = '') {
2713
		$size = false;
2714
		if ($mime === '' || strtolower(substr($mime, 0, 5)) === 'image') {
2715
			if ($work = $this->getWorkFile($path)) {
2716
				if ($size = @getimagesize($work)) {
2717
					$size['dimensions'] = $size[0].'x'.$size[1];
2718
				}
2719
			}
2720
			is_file($work) && @unlink($work);
2721
		}
2722
		return $size;
2723
	}
2724
	
2725
	/**
2726
	 * Delete dirctory trees
2727
	 *
2728
	 * @param string $localpath path need convert encoding to server encoding
2729
	 * @return boolean
2730
	 * @author Naoki Sawada
2731
	 */
2732
	protected function delTree($localpath) {
2733
		foreach ($this->_scandir($localpath) as $p) {
2734
			@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...
2735
			$stat = $this->stat($this->convEncOut($p));
2736
			$this->convEncIn();
2737
			($stat['mime'] === 'directory')? $this->delTree($p) : $this->_unlink($p);
2738
		}
2739
		return $this->_rmdir($localpath);
2740
	}
2741
	
2742
	/*********************** file stat *********************/
2743
	
2744
	/**
2745
	 * Check file attribute
2746
	 *
2747
	 * @param  string  $path  file path
2748
	 * @param  string  $name  attribute name (read|write|locked|hidden)
2749
	 * @param  bool    $val   attribute value returned by file system
2750
	 * @param  bool    $isDir path is directory (true: directory, false: file)
2751
	 * @return bool
2752
	 * @author Dmitry (dio) Levashov
2753
	 **/
2754
	protected function attr($path, $name, $val=null, $isDir=null) {
2755
		if (!isset($this->defaults[$name])) {
2756
			return false;
2757
		}
2758
		
2759
		
2760
		$perm = null;
2761
		
2762 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...
2763
			$perm = call_user_func($this->access, $name, $path, $this->options['accessControlData'], $this, $isDir);
2764
2765
			if ($perm !== null) {
2766
				return !!$perm;
2767
			}
2768
		}
2769
		
2770
		if ($this->separator != '/') {
2771
			$path = str_replace($this->separator, '/', $this->relpathCE($path));
2772
		} else {
2773
			$path = $this->relpathCE($path);
2774
		}
2775
2776
		$path = '/'.$path;
2777
2778 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...
2779
			$attrs = $this->attributes[$i];
2780
			
2781
			if (isset($attrs[$name]) && isset($attrs['pattern']) && preg_match($attrs['pattern'], $path)) {
2782
				$perm = $attrs[$name];
2783
			} 
2784
		}
2785
		
2786
		return $perm === null ? (is_null($val)? $this->defaults[$name] : $val) : !!$perm;
2787
	}
2788
	
2789
	/**
2790
	 * Return true if file with given name can be created in given folder.
2791
	 *
2792
	 * @param string $dir  parent dir path
2793
	 * @param string $name new file name
2794
	 * @return bool
2795
	 * @author Dmitry (dio) Levashov
2796
	 **/
2797
	protected function allowCreate($dir, $name, $isDir = null) {
2798
		$path = $this->joinPathCE($dir, $name);
2799
		$perm = null;
2800
		
2801 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...
2802
			$perm = call_user_func($this->access, 'write', $path, $this->options['accessControlData'], $this, $isDir);			
2803
			if ($perm !== null) {
2804
				return !!$perm;
2805
			}
2806
		}
2807
		
2808
		$testPath = $this->separator.$this->relpathCE($path);
2809
		
2810 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...
2811
			$attrs = $this->attributes[$i];
2812
			
2813
			if (isset($attrs['write']) && isset($attrs['pattern']) && preg_match($attrs['pattern'], $testPath)) {
2814
				$perm = $attrs['write'];
2815
			} 
2816
		}
2817
		
2818
		return $perm === null ? true : $perm;
2819
	}
2820
	
2821
	/**
2822
	 * Return true if file MIME type can save with check uploadOrder config.
2823
	 * 
2824
	 * @param string $mime
2825
	 * @return boolean
2826
	 */
2827
	protected function allowPutMime($mime) {
2828
		// logic based on http://httpd.apache.org/docs/2.2/mod/mod_authz_host.html#order
2829
		$allow  = $this->mimeAccepted($mime, $this->uploadAllow, null);
2830
		$deny   = $this->mimeAccepted($mime, $this->uploadDeny,  null);
2831
		$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...
2832
		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...
2833
			$res = false; // default is deny
2834
			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...
2835
				$res = true;
2836
			}// else (both match | no match | match only deny) { deny }
2837
		} else { // array('deny', 'allow'), default is to 'allow' - this is the default rule
2838
			$res = true; // default is allow
2839
			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...
2840
				$res = false;
2841
			} // else (both match | no match | match only allow) { allow }
2842
		}
2843
		return $res;
2844
	}
2845
	
2846
	/**
2847
	 * Return fileinfo 
2848
	 *
2849
	 * @param  string  $path  file cache
2850
	 * @return array
2851
	 * @author Dmitry (dio) Levashov
2852
	 **/
2853
	protected function stat($path) {
2854
		if ($path === false || is_null($path)) {
2855
			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...
2856
		}
2857
		$is_root = ($path == $this->root);
2858
		if ($is_root) {
2859
			$rootKey = md5($path);
2860
			if (!isset($this->sessionCache['rootstat'])) {
2861
				$this->sessionCache['rootstat'] = array();
2862
			}
2863
			if (! $this->isMyReload()) {
2864
				// need $path as key for netmount/netunmount
2865
				if (isset($this->sessionCache['rootstat'][$rootKey])) {
2866
					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...
2867
						return $ret;
2868
					}
2869
				}
2870
			}
2871
		}
2872
		$ret = isset($this->cache[$path])
2873
			? $this->cache[$path]
2874
			: $this->updateCache($path, $this->convEncOut($this->_stat($this->convEncIn($path))));
2875
		if ($is_root) {
2876
			$this->sessionRestart();
2877
			$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...
2878
		}
2879
		return $ret;
2880
	}
2881
	
2882
	/**
2883
	 * Put file stat in cache and return it
2884
	 *
2885
	 * @param  string  $path   file path
2886
	 * @param  array   $stat   file stat
2887
	 * @return array
2888
	 * @author Dmitry (dio) Levashov
2889
	 **/
2890
	protected function updateCache($path, $stat) {
2891
		if (empty($stat) || !is_array($stat)) {
2892
			return $this->cache[$path] = array();
2893
		}
2894
2895
		$stat['hash'] = $this->encode($path);
2896
2897
		$root = $path == $this->root;
2898
		$parent = '';
2899
		
2900
		if ($root) {
2901
			if ($this->rootName) {
2902
				$stat['name'] = $this->rootName;
2903
			}
2904
			if (! empty($this->options['icon'])) {
2905
				$stat['icon'] = $this->options['icon'];
2906
			}
2907
			if (! empty($this->options['rootCssClass'])) {
2908
				$stat['csscls'] = $this->options['rootCssClass'];
2909
			}
2910
		} else {
2911
			if (!isset($stat['name']) || $stat['name'] === '') {
2912
				$stat['name'] = $this->basenameCE($path);
2913
			}
2914
			if (empty($stat['phash'])) {
2915
				$parent = $this->dirnameCE($path);
2916
				$stat['phash'] = $this->encode($parent);
2917
			}
2918
		}
2919
		
2920
		// name check
2921
		if (!$jeName = json_encode($stat['name'])) {
2922
			return $this->cache[$path] = array();
2923
		}
2924
		// fix name if required
2925
		if ($this->options['utf8fix'] && $this->options['utf8patterns'] && $this->options['utf8replace']) {
2926
			$stat['name'] = json_decode(str_replace($this->options['utf8patterns'], $this->options['utf8replace'], $jeName));
2927
		}
2928
		
2929
		
2930
		if (empty($stat['mime'])) {
2931
			$stat['mime'] = $this->mimetype($stat['name']);
2932
		}
2933
		
2934
		// @todo move dateformat to client
2935
		// $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...
2936
		// 	? $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...
2937
		// 	: 'unknown';
2938
			
2939
		if (!isset($stat['size'])) {
2940
			$stat['size'] = 'unknown';
2941
		}	
2942
2943
		if ($isDir = ($stat['mime'] === 'directory')) {
2944
			$stat['volumeid'] = $this->id;
2945
		}
2946
		
2947
		$stat['read']  = intval($this->attr($path, 'read', isset($stat['read']) ? !!$stat['read'] : null, $isDir));
2948
		$stat['write'] = intval($this->attr($path, 'write', isset($stat['write']) ? !!$stat['write'] : null, $isDir));
2949
		if ($root) {
2950
			$stat['locked'] = 1;
2951
		} else {
2952
			// lock when parent directory is not writable
2953
			if (!isset($stat['locked'])) {
2954
				$parent = $this->dirnameCE($path);
2955
				$pstat = isset($this->cache[$parent])? $this->cache[$parent] : array();
2956
				if (isset($pstat['write']) && !$pstat['write']) {
2957
					$stat['locked'] = true;
2958
				}
2959
			}
2960
			if ($this->attr($path, 'locked', isset($stat['locked']) ? !!$stat['locked'] : null, $isDir)) {
2961
				$stat['locked'] = 1;
2962
			} else {
2963
				unset($stat['locked']);
2964
			}
2965
		}
2966
2967
		if ($root) {
2968
			unset($stat['hidden']);
2969
		} elseif ($this->attr($path, 'hidden', isset($stat['hidden']) ? !!$stat['hidden'] : null, $isDir) 
2970
		|| !$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...
2971
			$stat['hidden'] = 1;
2972
		} else {
2973
			unset($stat['hidden']);
2974
		}
2975
		
2976
		if ($stat['read'] && empty($stat['hidden'])) {
2977
			
2978
			if ($isDir) {
2979
				// caching parent's subdirs
2980
				if ($parent) {
2981
					$this->subdirsCache[$parent] = true;
2982
				}
2983
				// for dir - check for subdirs
2984
				if ($this->options['checkSubfolders']) {
2985
					if (isset($stat['dirs'])) {
2986
						if ($stat['dirs']) {
2987
							$stat['dirs'] = 1;
2988
						} else {
2989
							unset($stat['dirs']);
2990
						}
2991
					} elseif (!empty($stat['alias']) && !empty($stat['target'])) {
2992
						$stat['dirs'] = isset($this->cache[$stat['target']])
2993
							? intval(isset($this->cache[$stat['target']]['dirs']))
2994
							: $this->subdirsCE($stat['target']);
2995
						
2996
					} elseif ($this->subdirsCE($path)) {
2997
						$stat['dirs'] = 1;
2998
					}
2999
				} else {
3000
					$stat['dirs'] = 1;
3001
				}
3002
			} else {
3003
				// for files - check for thumbnails
3004
				$p = isset($stat['target']) ? $stat['target'] : $path;
3005
				if ($this->tmbURL && !isset($stat['tmb']) && $this->canCreateTmb($p, $stat)) {
3006
					$tmb = $this->gettmb($p, $stat);
3007
					$stat['tmb'] = $tmb ? $tmb : 1;
3008
				}
3009
				
3010
			}
3011
			if (!isset($stat['url']) && $this->URL && $this->encoding) {
3012
				$_path = str_replace($this->separator, '/', substr($path, strlen($this->root) + 1));
3013
				$stat['url'] = rtrim($this->URL, '/') . '/' . str_replace('%2F', '/', rawurlencode((substr(PHP_OS, 0, 3) === 'WIN')? $_path : $this->convEncIn($_path, true)));
3014
			}
3015
		} else {
3016
			if ($isDir) {
3017
				unset($stat['dirs']);
3018
			}
3019
		}
3020
		
3021
		if (!empty($stat['alias']) && !empty($stat['target'])) {
3022
			$stat['thash'] = $this->encode($stat['target']);
3023
			//$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...
3024
			unset($stat['target']);
3025
		}
3026
		
3027
		if (isset($this->options['netkey']) && $path === $this->root) {
3028
			$stat['netkey'] = $this->options['netkey'];
3029
		}
3030
		
3031
		return $this->cache[$path] = $stat;
3032
	}
3033
	
3034
	/**
3035
	 * Get stat for folder content and put in cache
3036
	 *
3037
	 * @param  string  $path
3038
	 * @return void
3039
	 * @author Dmitry (dio) Levashov
3040
	 **/
3041
	protected function cacheDir($path) {
3042
		$this->dirsCache[$path] = array();
3043
		$this->subdirsCache[$path] = false;
3044
3045
		foreach ($this->scandirCE($path) as $p) {
3046
			if (($stat = $this->stat($p)) && empty($stat['hidden'])) {
3047
				if ($stat['mime'] === 'directory') {
3048
					$this->subdirsCache[$path] = true;
3049
				}
3050
				$this->dirsCache[$path][] = $p;
3051
			}
3052
		}
3053
	}
3054
	
3055
	/**
3056
	 * Clean cache
3057
	 *
3058
	 * @return void
3059
	 * @author Dmitry (dio) Levashov
3060
	 **/
3061
	protected function clearcache() {
3062
		$this->cache = $this->dirsCache = array();
3063
		$this->sessionRestart();
3064
		unset($this->sessionCache['rootstat'][md5($this->root)]);
3065
	}
3066
	
3067
	/**
3068
	 * Return file mimetype
3069
	 *
3070
	 * @param  string  $path  file path
3071
	 * @return string
3072
	 * @author Dmitry (dio) Levashov
3073
	 **/
3074
	protected function mimetype($path, $name = '') {
3075
		$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...
3076
		
3077
		if ($name === '') {
3078
			$name = $path;
3079
		}
3080
		$ext = (false === $pos = strrpos($name, '.')) ? '' : substr($name, $pos + 1);
3081
		if ($this->mimeDetect == 'finfo') {
3082
			if ($type = @finfo_file($this->finfo, $path)) {
3083
				if ($ext && preg_match('~^application/(?:octet-stream|(?:x-)?zip)~', $type)) {
3084
					if (isset(elFinderVolumeDriver::$mimetypes[$ext])) $type = elFinderVolumeDriver::$mimetypes[$ext];
3085
				} else if ($ext === 'js' && preg_match('~^text/~', $type)) {
3086
					$type = 'text/javascript';
3087
				}
3088
			} else {
3089
				$type = 'unknown';
3090
			}
3091
		} elseif ($this->mimeDetect == 'mime_content_type') {
3092
			$type = mime_content_type($path);
3093
		} else {
3094
			$type = elFinderVolumeDriver::mimetypeInternalDetect($path);
3095
		}
3096
		
3097
		$type = explode(';', $type);
3098
		$type = trim($type[0]);
3099
3100 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...
3101
			// finfo return this mime for empty files
3102
			$type = 'text/plain';
3103
		} elseif ($type == 'application/x-zip') {
3104
			// http://elrte.org/redmine/issues/163
3105
			$type = 'application/zip';
3106
		}
3107
		
3108
		// mime type normalization
3109
		$_checkKey = strtolower($ext.':'.$type);
3110
		if (isset($this->options['mimeMap'][$_checkKey])) {
3111
			$type = $this->options['mimeMap'][$_checkKey];
3112
		}
3113
		
3114
		return $type == 'unknown' && $this->mimeDetect != 'internal'
3115
			? elFinderVolumeDriver::mimetypeInternalDetect($path)
3116
			: $type;
3117
		
3118
	}
3119
	
3120
	/**
3121
	 * Detect file mimetype using "internal" method
3122
	 *
3123
	 * @param  string  $path  file path
3124
	 * @return string
3125
	 * @author Dmitry (dio) Levashov
3126
	 **/
3127
	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...
3128
		// load default MIME table file "mime.types"
3129
		if (!elFinderVolumeDriver::$mimetypesLoaded) {
3130
			elFinderVolumeDriver::$mimetypesLoaded = true;
3131
			$file = dirname(__FILE__).DIRECTORY_SEPARATOR.'mime.types';
3132 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...
3133
				$mimecf = file($file);
3134
				foreach ($mimecf as $line_num => $line) {
3135
					if (!preg_match('/^\s*#/', $line)) {
3136
						$mime = preg_split('/\s+/', $line, -1, PREG_SPLIT_NO_EMPTY);
3137
						for ($i = 1, $size = count($mime); $i < $size ; $i++) {
3138
							if (!isset(elFinderVolumeDriver::$mimetypes[$mime[$i]])) {
3139
								elFinderVolumeDriver::$mimetypes[$mime[$i]] = $mime[0];
3140
							}
3141
						}
3142
					}
3143
				}
3144
			}
3145
		}
3146
		$pinfo = pathinfo($path); 
3147
		$ext   = isset($pinfo['extension']) ? strtolower($pinfo['extension']) : '';
3148
		return isset(elFinderVolumeDriver::$mimetypes[$ext]) ? elFinderVolumeDriver::$mimetypes[$ext] : 'unknown';
3149
	}
3150
	
3151
	/**
3152
	 * Return file/total directory size
3153
	 *
3154
	 * @param  string  $path  file path
3155
	 * @return int
3156
	 * @author Dmitry (dio) Levashov
3157
	 **/
3158
	protected function countSize($path) {
3159
		$stat = $this->stat($path);
3160
3161
		if (empty($stat) || !$stat['read'] || !empty($stat['hidden'])) {
3162
			return 'unknown';
3163
		}
3164
		
3165
		if ($stat['mime'] != 'directory') {
3166
			return $stat['size'];
3167
		}
3168
		
3169
		$subdirs = $this->options['checkSubfolders'];
3170
		$this->options['checkSubfolders'] = true;
3171
		$result = 0;
3172
		foreach ($this->getScandir($path) as $stat) {
3173
			$size = $stat['mime'] == 'directory' && $stat['read'] 
3174
				? $this->countSize($this->joinPathCE($path, $stat['name'])) 
3175
				: (isset($stat['size']) ? intval($stat['size']) : 0);
3176
			if ($size > 0) {
3177
				$result += $size;
3178
			}
3179
		}
3180
		$this->options['checkSubfolders'] = $subdirs;
3181
		return $result;
3182
	}
3183
	
3184
	/**
3185
	 * Return true if all mimes is directory or files
3186
	 *
3187
	 * @param  string  $mime1  mimetype
3188
	 * @param  string  $mime2  mimetype
3189
	 * @return bool
3190
	 * @author Dmitry (dio) Levashov
3191
	 **/
3192
	protected function isSameType($mime1, $mime2) {
3193
		return ($mime1 == 'directory' && $mime1 == $mime2) || ($mime1 != 'directory' && $mime2 != 'directory');
3194
	}
3195
	
3196
	/**
3197
	 * If file has required attr == $val - return file path,
3198
	 * If dir has child with has required attr == $val - return child path
3199
	 *
3200
	 * @param  string   $path  file path
3201
	 * @param  string   $attr  attribute name
3202
	 * @param  bool     $val   attribute value
3203
	 * @return string|false
3204
	 * @author Dmitry (dio) Levashov
3205
	 **/
3206
	protected function closestByAttr($path, $attr, $val) {
3207
		$stat = $this->stat($path);
3208
		
3209
		if (empty($stat)) {
3210
			return false;
3211
		}
3212
		
3213
		$v = isset($stat[$attr]) ? $stat[$attr] : false;
3214
		
3215
		if ($v == $val) {
3216
			return $path;
3217
		}
3218
3219
		return $stat['mime'] == 'directory'
3220
			? $this->childsByAttr($path, $attr, $val) 
3221
			: false;
3222
	}
3223
	
3224
	/**
3225
	 * Return first found children with required attr == $val
3226
	 *
3227
	 * @param  string   $path  file path
3228
	 * @param  string   $attr  attribute name
3229
	 * @param  bool     $val   attribute value
3230
	 * @return string|false
3231
	 * @author Dmitry (dio) Levashov
3232
	 **/
3233
	protected function childsByAttr($path, $attr, $val) {
3234
		foreach ($this->scandirCE($path) as $p) {
3235
			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...
3236
				return $_p;
3237
			}
3238
		}
3239
		return false;
3240
	}
3241
	
3242
	protected function isMyReload($target = '', $ARGtarget = '') {
3243
		if (! empty($this->ARGS['cmd']) && $this->ARGS['cmd'] === 'parents') {
3244
			return true;
3245
		}
3246
		if (! empty($this->ARGS['reload'])) {
3247
			if ($ARGtarget === '') {
3248
				$ARGtarget = isset($this->ARGS['target'])? $this->ARGS['target']
3249
					: ((isset($this->ARGS['targets']) && is_array($this->ARGS['targets']) && count($this->ARGS['targets']) === 1)?
3250
						$this->ARGS['targets'][0] : '');
3251
			}
3252
			if ($ARGtarget !== '') {
3253
				$ARGtarget = strval($ARGtarget);
3254
				if ($target === '') {
3255
					return (strpos($ARGtarget, $this->id) === 0);
3256
				} else {
3257
					$target = strval($target);
3258
					return ($target === $ARGtarget);
3259
				}
3260
			}
3261
		}
3262
		return false;
3263
	}
3264
	
3265
	/*****************  get content *******************/
3266
	
3267
	/**
3268
	 * Return required dir's files info.
3269
	 * If onlyMimes is set - return only dirs and files of required mimes
3270
	 *
3271
	 * @param  string  $path  dir path
3272
	 * @return array
3273
	 * @author Dmitry (dio) Levashov
3274
	 **/
3275
	protected function getScandir($path) {
3276
		$files = array();
3277
		
3278
		!isset($this->dirsCache[$path]) && $this->cacheDir($path);
3279
3280
		foreach ($this->dirsCache[$path] as $p) {
3281
			if (($stat = $this->stat($p)) && empty($stat['hidden'])) {
3282
				$files[] = $stat;
3283
			}
3284
		}
3285
3286
		return $files;
3287
	}
3288
	
3289
	
3290
	/**
3291
	 * Return subdirs tree
3292
	 *
3293
	 * @param  string  $path  parent dir path
3294
	 * @param  int     $deep  tree deep
3295
	 * @return array
3296
	 * @author Dmitry (dio) Levashov
3297
	 **/
3298
	protected function gettree($path, $deep, $exclude='') {
3299
		$dirs = array();
3300
		
3301
		!isset($this->dirsCache[$path]) && $this->cacheDir($path);
3302
3303
		foreach ($this->dirsCache[$path] as $p) {
3304
			$stat = $this->stat($p);
3305
			
3306
			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...
3307
				$dirs[] = $stat;
3308
				if ($deep > 0 && !empty($stat['dirs'])) {
3309
					$dirs = array_merge($dirs, $this->gettree($p, $deep-1));
3310
				}
3311
			}
3312
		}
3313
3314
		return $dirs;
3315
	}	
3316
		
3317
	/**
3318
	 * Recursive files search
3319
	 *
3320
	 * @param  string  $path   dir path
3321
	 * @param  string  $q      search string
3322
	 * @param  array   $mimes
3323
	 * @return array
3324
	 * @author Dmitry (dio) Levashov
3325
	 **/
3326
	protected function doSearch($path, $q, $mimes) {
3327
		$result = array();
3328
		
3329
		$timeout = $this->options['searchTimeout']? $this->searchStart + $this->options['searchTimeout'] : 0;
3330 View Code Duplication
		if ($timeout && $timeout < time()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3331
			$this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($path)));
3332
			return $result;
3333
		}
3334
		
3335
		foreach($this->scandirCE($path) as $p) {
3336
			@set_time_limit($this->options['searchTimeout'] + 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...
3337
			
3338
			if ($timeout && ($this->error || $timeout < time())) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->error of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
3339
				!$this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($path)));
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->error of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
3340
				break;
3341
			}
3342
3343
			
3344
			$stat = $this->stat($p);
3345
3346
			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...
3347
				continue;
3348
			}
3349
3350
			if (!empty($stat['hidden']) || !$this->mimeAccepted($stat['mime'], $mimes)) {
3351
				continue;
3352
			}
3353
			
3354
			$name = $stat['name'];
3355
3356
			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...
3357
				$stat['path'] = $this->path($stat['hash']);
3358
				if ($this->URL && !isset($stat['url'])) {
3359
					$path = str_replace($this->separator, '/', substr($p, strlen($this->root) + 1));
3360
					if ($this->encoding) {
3361
						$path = str_replace('%2F', '/', rawurlencode($this->convEncIn($path, true)));
3362
					}
3363
					$stat['url'] = $this->URL . $path;
3364
				}
3365
				
3366
				$result[] = $stat;
3367
			}
3368
			if ($stat['mime'] == 'directory' && $stat['read'] && !isset($stat['alias'])) {
3369
				$result = array_merge($result, $this->doSearch($p, $q, $mimes));
3370
			}
3371
		}
3372
		
3373
		return $result;
3374
	}
3375
		
3376
	/**********************  manuipulations  ******************/
3377
		
3378
	/**
3379
	 * Copy file/recursive copy dir only in current volume.
3380
	 * Return new file path or false.
3381
	 *
3382
	 * @param  string  $src   source path
3383
	 * @param  string  $dst   destination dir path
3384
	 * @param  string  $name  new file name (optionaly)
3385
	 * @return string|false
3386
	 * @author Dmitry (dio) Levashov
3387
	 **/
3388
	protected function copy($src, $dst, $name) {
3389
		$srcStat = $this->stat($src);
3390
		$this->clearcache();
3391
		
3392
		if (!empty($srcStat['thash'])) {
3393
			$target = $this->decode($srcStat['thash']);
3394
			if (!$this->inpathCE($target, $this->root)) {
3395
				return $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']), elFinder::ERROR_MKOUTLINK);
3396
			}
3397
			$stat   = $this->stat($target);
3398
			$this->clearcache();
3399
			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...
3400
				? $this->joinPathCE($dst, $name)
3401
				: $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']));
3402
		} 
3403
		
3404
		if ($srcStat['mime'] == 'directory') {
3405
			$test = $this->stat($this->joinPathCE($dst, $name));
3406
			
3407 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...
3408
				return $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']));
3409
			}
3410
			
3411
			$dst = $this->joinPathCE($dst, $name);
3412
			
3413
			foreach ($this->getScandir($src) as $stat) {
3414
				if (empty($stat['hidden'])) {
3415
					$name = $stat['name'];
3416
					if (!$this->copy($this->joinPathCE($src, $name), $dst, $name)) {
3417
						$this->remove($dst, true); // fall back
3418
						return $this->setError($this->error, elFinder::ERROR_COPY, $this->_path($src));
3419
					}
3420
				}
3421
			}
3422
			$this->clearcache();
3423
			return $dst;
3424
		} 
3425
3426
		return $this->convEncOut($this->_copy($this->convEncIn($src), $this->convEncIn($dst), $this->convEncIn($name)))
3427
			? $this->joinPathCE($dst, $name) 
3428
			: $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']));
3429
	}
3430
3431
	/**
3432
	 * Move file
3433
	 * Return new file path or false.
3434
	 *
3435
	 * @param  string  $src   source path
3436
	 * @param  string  $dst   destination dir path
3437
	 * @param  string  $name  new file name 
3438
	 * @return string|false
3439
	 * @author Dmitry (dio) Levashov
3440
	 **/
3441
	protected function move($src, $dst, $name) {
3442
		$stat = $this->stat($src);
3443
		$stat['realpath'] = $src;
3444
		$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...
3445
		$this->clearcache();
3446
		
3447 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...
3448
			$this->removed[] = $stat;
3449
3450
			return $this->joinPathCE($dst, $name);
3451
		}
3452
3453
		return $this->setError(elFinder::ERROR_MOVE, $this->path($stat['hash']));
3454
	}
3455
3456
	/**
3457
	 * Copy file from another volume.
3458
	 * Return new file path or false.
3459
	 *
3460
	 * @param  Object  $volume       source volume
3461
	 * @param  string  $src          source file hash
3462
	 * @param  string  $destination  destination dir path
3463
	 * @param  string  $name         file name
3464
	 * @return string|false
3465
	 * @author Dmitry (dio) Levashov
3466
	 **/
3467
	protected function copyFrom($volume, $src, $destination, $name) {
3468
		
3469
		if (($source = $volume->file($src)) == false) {
3470
			return $this->setError(elFinder::ERROR_COPY, '#'.$src, $volume->error());
3471
		}
3472
		
3473
		$errpath = $volume->path($source['hash']);
3474
		
3475
		if (!$this->nameAccepted($source['name'])) {
3476
			return $this->setError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_INVALID_NAME);
3477
		}
3478
				
3479
		if (!$source['read']) {
3480
			return $this->setError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_PERM_DENIED);
3481
		}
3482
		
3483
		if ($source['mime'] == 'directory') {
3484
			$stat = $this->stat($this->joinPathCE($destination, $name));
3485
			$this->clearcache();
3486 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...
3487
				return $this->setError(elFinder::ERROR_COPY, $errpath);
3488
			}
3489
			
3490
			$path = $this->joinPathCE($destination, $name);
3491
			
3492
			foreach ($volume->scandir($src) as $entr) {
3493
				if (!$this->copyFrom($volume, $entr['hash'], $path, $entr['name'])) {
3494
					$this->remove($path, true); // fall back
3495
					return $this->setError($this->error, elFinder::ERROR_COPY, $errpath);
3496
				}
3497
			}
3498
			
3499
		} else {
3500
			// $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...
3501
			// $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...
3502
			if (($dim = $volume->dimensions($src))) {
3503
				$s = explode('x', $dim);
3504
				$source['width']  = $s[0];
3505
				$source['height'] = $s[1];
3506
			}
3507
			
3508
			if (($fp = $volume->open($src)) == false
3509
			|| ($path = $this->saveCE($fp, $destination, $name, $source)) == false) {
3510
				$fp && $volume->close($fp, $src);
3511
				return $this->setError(elFinder::ERROR_COPY, $errpath);
3512
			}
3513
			$volume->close($fp, $src);
3514
			
3515
			// MIME check
3516
			$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 3509 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...
3517
			$mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($stat['name']);
3518
			if ($stat['mime'] === $mimeByName) {
3519
				$mimeByName = '';
3520
			}
3521
			if (!$this->allowPutMime($stat['mime']) || ($mimeByName && $mimeByName !== 'unknown' && !$this->allowPutMime($mimeByName))) {
3522
				$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 3509 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...
3523
				return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME, $errpath);
3524
			}
3525
		}
3526
		
3527
		return $path;
3528
	}
3529
		
3530
	/**
3531
	 * Remove file/ recursive remove dir
3532
	 *
3533
	 * @param  string  $path   file path
3534
	 * @param  bool    $force  try to remove even if file locked
3535
	 * @return bool
3536
	 * @author Dmitry (dio) Levashov
3537
	 **/
3538
	protected function remove($path, $force = false) {
3539
		$stat = $this->stat($path);
3540
		
3541
		if (empty($stat)) {
3542
			return $this->setError(elFinder::ERROR_RM, $this->path($stat['hash']), elFinder::ERROR_FILE_NOT_FOUND);
3543
		}
3544
		
3545
		$stat['realpath'] = $path;
3546
		$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...
3547
		$this->clearcache();
3548
		
3549 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...
3550
			return $this->setError(elFinder::ERROR_LOCKED, $this->path($stat['hash']));
3551
		}
3552
		
3553
		if ($stat['mime'] == 'directory' && empty($stat['thash'])) {
3554
			$ret = $this->delTree($this->convEncIn($path));
3555
			$this->convEncOut();
3556
			if (!$ret) {
3557
				return $this->setError(elFinder::ERROR_RM, $this->path($stat['hash']));
3558
			}
3559 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...
3560
			if ($this->convEncOut(!$this->_unlink($this->convEncIn($path)))) {
3561
				return $this->setError(elFinder::ERROR_RM, $this->path($stat['hash']));
3562
			}
3563
		}
3564
3565
		$this->removed[] = $stat;
3566
		return true;
3567
	}
3568
	
3569
3570
	/************************* thumbnails **************************/
3571
		
3572
	/**
3573
	 * Return thumbnail file name for required file
3574
	 *
3575
	 * @param  array  $stat  file stat
3576
	 * @return string
3577
	 * @author Dmitry (dio) Levashov
3578
	 **/
3579
	protected function tmbname($stat) {
3580
		return $stat['hash'].$stat['ts'].'.png';
3581
	}
3582
	
3583
	/**
3584
	 * Return thumnbnail name if exists
3585
	 *
3586
	 * @param  string  $path file path
3587
	 * @param  array   $stat file stat
3588
	 * @return string|false
3589
	 * @author Dmitry (dio) Levashov
3590
	 **/
3591
	protected function gettmb($path, $stat) {
3592
		if ($this->tmbURL && $this->tmbPath) {
3593
			// file itself thumnbnail
3594
			if (strpos($path, $this->tmbPath) === 0) {
3595
				return basename($path);
3596
			}
3597
3598
			$name = $this->tmbname($stat);
3599
			if (file_exists($this->tmbPath.DIRECTORY_SEPARATOR.$name)) {
3600
				return $name;
3601
			}
3602
		}
3603
		return false;
3604
	}
3605
	
3606
	/**
3607
	 * Return true if thumnbnail for required file can be created
3608
	 *
3609
	 * @param  string  $path  thumnbnail path 
3610
	 * @param  array   $stat  file stat
3611
	 * @param  bool    $checkTmbPath
3612
	 * @return string|bool
3613
	 * @author Dmitry (dio) Levashov
3614
	 **/
3615
	protected function canCreateTmb($path, $stat, $checkTmbPath = true) {
3616
		return (!$checkTmbPath || $this->tmbPathWritable) 
3617
			&& (!$this->tmbPath || strpos($path, $this->tmbPath) === false) // do not create thumnbnail for thumnbnail
3618
			&& $this->imgLib 
3619
			&& strpos($stat['mime'], 'image') === 0 
3620
			&& ($this->imgLib == 'gd' ? $stat['mime'] == 'image/jpeg' || $stat['mime'] == 'image/png' || $stat['mime'] == 'image/gif' : true);
3621
	}
3622
	
3623
	/**
3624
	 * Return true if required file can be resized.
3625
	 * By default - the same as canCreateTmb
3626
	 *
3627
	 * @param  string  $path  thumnbnail path 
3628
	 * @param  array   $stat  file stat
3629
	 * @return string|bool
3630
	 * @author Dmitry (dio) Levashov
3631
	 **/
3632
	protected function canResize($path, $stat) {
3633
		return $this->canCreateTmb($path, $stat, false);
3634
	}
3635
	
3636
	/**
3637
	 * Create thumnbnail and return it's URL on success
3638
	 *
3639
	 * @param  string  $path  file path
3640
	 * @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...
3641
	 * @return string|false
3642
	 * @author Dmitry (dio) Levashov
3643
	 **/
3644
	protected function createTmb($path, $stat) {
3645
		if (!$stat || !$this->canCreateTmb($path, $stat)) {
3646
			return false;
3647
		}
3648
3649
		$name = $this->tmbname($stat);
3650
		$tmb  = $this->tmbPath.DIRECTORY_SEPARATOR.$name;
3651
3652
		// copy image into tmbPath so some drivers does not store files on local fs
3653
		if (($src = $this->fopenCE($path, 'rb')) == false) {
3654
			return false;
3655
		}
3656
3657
		if (($trg = fopen($tmb, 'wb')) == false) {
3658
			$this->fcloseCE($src, $path);
3659
			return false;
3660
		}
3661
3662
		while (!feof($src)) {
3663
			fwrite($trg, fread($src, 8192));
3664
		}
3665
3666
		$this->fcloseCE($src, $path);
3667
		fclose($trg);
3668
3669
		$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...
3670
		
3671
		$tmbSize = $this->tmbSize;
3672
		
3673
		if (($s = getimagesize($tmb)) == false) {
3674
			return false;
3675
		}
3676
3677
		/* If image smaller or equal thumbnail size - just fitting to thumbnail square */
3678
		if ($s[0] <= $tmbSize && $s[1]	<= $tmbSize) {
3679
			$result = $this->imgSquareFit($tmb, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png' );
3680
		} else {
3681
		
3682
			if ($this->options['tmbCrop']) {
3683
		
3684
				$result = $tmb;
3685
				/* Resize and crop if image bigger than thumbnail */
3686 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...
3687
					$result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, false, 'png');
3688
				}
3689
		
3690 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...
3691
					$x = $s[0] > $tmbSize ? intval(($s[0] - $tmbSize)/2) : 0;
3692
					$y = $s[1] > $tmbSize ? intval(($s[1] - $tmbSize)/2) : 0;
3693
					$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...
3694
				} else {
3695
					$result = false;
3696
				}
3697
		
3698
			} else {
3699
				$result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, true, 'png');
3700
			}
3701
		
3702
			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...
3703
				$result = $this->imgSquareFit($result, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png' );
3704
			}
3705
		}
3706
		
3707
		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...
3708
			unlink($tmb);
3709
			return false;
3710
		}
3711
		
3712
		return $name;
3713
	}
3714
3715
	/**
3716
	 * Resize image
3717
	 *
3718
	 * @param  string   $path               image file
3719
	 * @param  int      $width              new width
3720
	 * @param  int      $height             new height
3721
	 * @param  bool	    $keepProportions    crop image
3722
	 * @param  bool	    $resizeByBiggerSide resize image based on bigger side if true
3723
	 * @param  string   $destformat         image destination format
3724
	 * @param  int      $jpgQuality         JEPG quality (1-100)
3725
	 * @return string|false
3726
	 * @author Dmitry (dio) Levashov
3727
	 * @author Alexey Sukhotin
3728
	 **/
3729
	protected function imgResize($path, $width, $height, $keepProportions = false, $resizeByBiggerSide = true, $destformat = null, $jpgQuality = null) {
3730
		if (($s = @getimagesize($path)) == false) {
3731
			return false;
3732
		}
3733
3734
		$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...
3735
		
3736
		list($size_w, $size_h) = array($width, $height);
3737
	
3738
		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...
3739
			$jpgQuality = $this->options['jpgQuality'];
3740
		}
3741
		
3742
		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...
3743
		
3744
			list($orig_w, $orig_h) = array($s[0], $s[1]);
3745
		
3746
			/* Resizing by biggest side */
3747
			if ($resizeByBiggerSide) {
3748 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...
3749
					$size_h = round($orig_h * $width / $orig_w);
3750
					$size_w = $width;
3751
				} else {
3752
					$size_w = round($orig_w * $height / $orig_h);
3753
					$size_h = $height;
3754
				}
3755 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...
3756
				if ($orig_w > $orig_h) {
3757
					$size_w = round($orig_w * $height / $orig_h);
3758
					$size_h = $height;
3759
				} else {
3760
					$size_h = round($orig_h * $width / $orig_w);
3761
					$size_w = $width;
3762
				}
3763
			}
3764
		}
3765
3766
		switch ($this->imgLib) {
3767
			case 'imagick':
3768
				
3769
				try {
3770
					$img = new imagick($path);
3771
				} catch (Exception $e) {
3772
					return false;
3773
				}
3774
3775
				// Imagick::FILTER_BOX faster than FILTER_LANCZOS so use for createTmb
3776
				// 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...
3777
				// 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...
3778
				$filter = ($destformat === 'png' /* createTmb */)? Imagick::FILTER_BOX : Imagick::FILTER_LANCZOS;
3779
				
3780
				$ani = ($img->getNumberImages() > 1);
3781
				if ($ani && is_null($destformat)) {
3782
					$img = $img->coalesceImages();
3783
					do {
3784
						$img->resizeImage($size_w, $size_h, $filter, 1);
3785
					} while ($img->nextImage());
3786
					$img = $img->optimizeImageLayers();
3787
					$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...
3788
				} else {
3789
					if ($ani) {
3790
						$img->setFirstIterator();
3791
					}
3792
					$img->resizeImage($size_w, $size_h, $filter, 1);
3793
					$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...
3794
				}
3795
				
3796
				$img->destroy();
3797
3798
				return $result ? $path : false;
3799
3800
				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...
3801
3802
			case 'gd':
3803
				$img = $this->gdImageCreate($path,$s['mime']);
3804
3805
				if ($img &&  false != ($tmp = imagecreatetruecolor($size_w, $size_h))) {
3806
				
3807
					$this->gdImageBackground($tmp,$this->options['tmbBgColor']);
3808
					
3809
					if (!imagecopyresampled($tmp, $img, 0, 0, 0, 0, $size_w, $size_h, $s[0], $s[1])) {
3810
						return false;
3811
					}
3812
		
3813
					$result = $this->gdImage($tmp, $path, $destformat, $s['mime'], $jpgQuality);
3814
3815
					imagedestroy($img);
3816
					imagedestroy($tmp);
3817
3818
					return $result ? $path : false;
3819
3820
				}
3821
				break;
3822
		}
3823
		
3824
		return false;
3825
  	}
3826
  
3827
	/**
3828
	 * Crop image
3829
	 *
3830
	 * @param  string   $path               image file
3831
	 * @param  int      $width              crop width
3832
	 * @param  int      $height             crop height
3833
	 * @param  bool	    $x                  crop left offset
3834
	 * @param  bool	    $y                  crop top offset
3835
	 * @param  string   $destformat         image destination format
3836
	 * @param  int      $jpgQuality         JEPG quality (1-100)
3837
	 * @return string|false
3838
	 * @author Dmitry (dio) Levashov
3839
	 * @author Alexey Sukhotin
3840
	 **/
3841
  	protected function imgCrop($path, $width, $height, $x, $y, $destformat = null, $jpgQuality = null) {
3842
		if (($s = @getimagesize($path)) == false) {
3843
			return false;
3844
		}
3845
3846
		$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...
3847
		
3848
		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...
3849
			$jpgQuality = $this->options['jpgQuality'];
3850
		}
3851
3852
		switch ($this->imgLib) {
3853
			case 'imagick':
3854
				
3855
				try {
3856
					$img = new imagick($path);
3857
				} catch (Exception $e) {
3858
					return false;
3859
				}
3860
				
3861
				$ani = ($img->getNumberImages() > 1);
3862
				if ($ani && is_null($destformat)) {
3863
					$img = $img->coalesceImages();
3864
					do {
3865
						$img->setImagePage($s[0], $s[1], 0, 0);
3866
						$img->cropImage($width, $height, $x, $y);
3867
						$img->setImagePage($width, $height, 0, 0);
3868
					} while ($img->nextImage());
3869
					$img = $img->optimizeImageLayers();
3870
					$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...
3871
				} else {
3872
					if ($ani) {
3873
						$img->setFirstIterator();
3874
					}
3875
					$img->setImagePage($s[0], $s[1], 0, 0);
3876
					$img->cropImage($width, $height, $x, $y);
3877
					$img->setImagePage($width, $height, 0, 0);
3878
					$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...
3879
				}
3880
				
3881
				$img->destroy();
3882
3883
				return $result ? $path : false;
3884
3885
				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...
3886
3887
			case 'gd':
3888
				$img = $this->gdImageCreate($path,$s['mime']);
3889
3890
				if ($img &&  false != ($tmp = imagecreatetruecolor($width, $height))) {
3891
					
3892
					$this->gdImageBackground($tmp,$this->options['tmbBgColor']);
3893
3894
					$size_w = $width;
3895
					$size_h = $height;
3896
3897
					if ($s[0] < $width || $s[1] < $height) {
3898
						$size_w = $s[0];
3899
						$size_h = $s[1];
3900
					}
3901
3902
					if (!imagecopy($tmp, $img, 0, 0, $x, $y, $size_w, $size_h)) {
3903
						return false;
3904
					}
3905
					
3906
					$result = $this->gdImage($tmp, $path, $destformat, $s['mime'], $jpgQuality);
3907
3908
					imagedestroy($img);
3909
					imagedestroy($tmp);
3910
3911
					return $result ? $path : false;
3912
3913
				}
3914
				break;
3915
		}
3916
3917
		return false;
3918
	}
3919
3920
	/**
3921
	 * Put image to square
3922
	 *
3923
	 * @param  string   $path               image file
3924
	 * @param  int      $width              square width
3925
	 * @param  int      $height             square height
3926
	 * @param  int	    $align              reserved
3927
	 * @param  int 	    $valign             reserved
3928
	 * @param  string   $bgcolor            square background color in #rrggbb format
3929
	 * @param  string   $destformat         image destination format
3930
	 * @param  int      $jpgQuality         JEPG quality (1-100)
3931
	 * @return string|false
3932
	 * @author Dmitry (dio) Levashov
3933
	 * @author Alexey Sukhotin
3934
	 **/
3935
	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...
3936
		if (($s = @getimagesize($path)) == false) {
3937
			return false;
3938
		}
3939
3940
		$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...
3941
3942
		/* Coordinates for image over square aligning */
3943
		$y = ceil(abs($height - $s[1]) / 2); 
3944
		$x = ceil(abs($width - $s[0]) / 2);
3945
3946
		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...
3947
			$jpgQuality = $this->options['jpgQuality'];
3948
		}
3949
3950
		switch ($this->imgLib) {
3951
			case 'imagick':
3952
				try {
3953
					$img = new imagick($path);
3954
				} catch (Exception $e) {
3955
					return false;
3956
				}
3957
				
3958
				$ani = ($img->getNumberImages() > 1);
3959
				if ($ani && is_null($destformat)) {
3960
					$img1 = new Imagick();
3961
					$img1->setFormat('gif');
3962
					$img = $img->coalesceImages();
3963
					do {
3964
						$gif = new Imagick();
3965
						$gif->newImage($width, $height, new ImagickPixel($bgcolor));
3966
						$gif->setImageColorspace($img->getImageColorspace());
3967
						$gif->setImageFormat('gif');
3968
						$gif->compositeImage( $img, imagick::COMPOSITE_OVER, $x, $y );
3969
						$gif->setImageDelay($img->getImageDelay());
3970
						$gif->setImageIterations($img->getImageIterations());
3971
						$img1->addImage($gif);
3972
						$gif->destroy();
3973
					} while ($img->nextImage());
3974
					$img1 = $img1->optimizeImageLayers();
3975
					$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...
3976
				} else {
3977
					if ($ani) {
3978
						$img->setFirstIterator();
3979
					}
3980
					$img1 = new Imagick();
3981
					$img1->newImage($width, $height, new ImagickPixel($bgcolor));
3982
					$img1->setImageColorspace($img->getImageColorspace());
3983
					$img1->compositeImage( $img, imagick::COMPOSITE_OVER, $x, $y );
3984
					$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...
3985
				}
3986
				
3987
				$img1->destroy();
3988
				$img->destroy();
3989
				return $result ? $path : false;
3990
3991
				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...
3992
3993
			case 'gd':
3994
				$img = $this->gdImageCreate($path,$s['mime']);
3995
3996
				if ($img &&  false != ($tmp = imagecreatetruecolor($width, $height))) {
3997
3998
					$this->gdImageBackground($tmp,$bgcolor);
3999
4000
					if (!imagecopy($tmp, $img, $x, $y, 0, 0, $s[0], $s[1])) {
4001
						return false;
4002
					}
4003
4004
					$result = $this->gdImage($tmp, $path, $destformat, $s['mime'], $jpgQuality);
4005
4006
					imagedestroy($img);
4007
					imagedestroy($tmp);
4008
4009
					return $result ? $path : false;
4010
				}
4011
				break;
4012
		}
4013
4014
		return false;
4015
	}
4016
4017
	/**
4018
	 * Rotate image
4019
	 *
4020
	 * @param  string   $path               image file
4021
	 * @param  int      $degree             rotete degrees
4022
	 * @param  string   $bgcolor            square background color in #rrggbb format
4023
	 * @param  string   $destformat         image destination format
4024
	 * @param  int      $jpgQuality         JEPG quality (1-100)
4025
	 * @return string|false
4026
	 * @author nao-pon
4027
	 * @author Troex Nevelin
4028
	 **/
4029
	protected function imgRotate($path, $degree, $bgcolor = '#ffffff', $destformat = null, $jpgQuality = null) {
4030
		if (($s = @getimagesize($path)) == false || $degree % 360 === 0) {
4031
			return false;
4032
		}
4033
4034
		$result = false;
4035
4036
		// try lossless rotate
4037
		if ($degree % 90 === 0 && in_array($s[2], array(IMAGETYPE_JPEG, IMAGETYPE_JPEG2000))) {
4038
			$count = ($degree / 90) % 4;
4039
			$exiftran = array(
4040
				1 => '-9',
4041
				2 => '-1',
4042
				3 => '-2'
4043
			);
4044
			$jpegtran = array(
4045
				1 => '90',
4046
				2 => '180',
4047
				3 => '270'
4048
			);
4049
			$quotedPath = escapeshellarg($path);
4050
			$cmds = array(
4051
				'exiftran -i '.$exiftran[$count].' '.$path,
4052
				'jpegtran -rotate '.$jpegtran[$count].' -copy all -outfile '.$quotedPath.' '.$quotedPath
4053
			);
4054
			foreach($cmds as $cmd) {
4055
				if ($this->procExec($cmd) === 0) {
4056
					$result = true;
4057
					break;
4058
				}
4059
			}
4060
			if ($result) {
4061
				return $path;
4062
			}
4063
		}
4064
4065
		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...
4066
			$jpgQuality = $this->options['jpgQuality'];
4067
		}
4068
4069
		switch ($this->imgLib) {
4070
			case 'imagick':
4071
				try {
4072
					$img = new imagick($path);
4073
				} catch (Exception $e) {
4074
					return false;
4075
				}
4076
4077
				if ($img->getNumberImages() > 1) {
4078
					$img = $img->coalesceImages();
4079
					do {
4080
						$img->rotateImage(new ImagickPixel($bgcolor), $degree);
4081
					} while ($img->nextImage());
4082
					$img = $img->optimizeImageLayers();
4083
					$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...
4084
				} else {
4085
					$img->rotateImage(new ImagickPixel($bgcolor), $degree);
4086
					$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...
4087
				}
4088
				$img->destroy();
4089
				return $result ? $path : false;
4090
4091
				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...
4092
4093
			case 'gd':
4094
				$img = $this->gdImageCreate($path,$s['mime']);
4095
4096
				$degree = 360 - $degree;
4097
				list($r, $g, $b) = sscanf($bgcolor, "#%02x%02x%02x");
4098
				$bgcolor = imagecolorallocate($img, $r, $g, $b);
4099
				$tmp = imageRotate($img, $degree, (int)$bgcolor);
4100
4101
				$result = $this->gdImage($tmp, $path, $destformat, $s['mime'], $jpgQuality);
4102
4103
				imageDestroy($img);
4104
				imageDestroy($tmp);
4105
4106
				return $result ? $path : false;
4107
4108
				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...
4109
		}
4110
4111
		return false;
4112
	}
4113
4114
	/**
4115
	 * Execute shell command
4116
	 *
4117
	 * @param  string  $command       command line
4118
	 * @param  array   $output        stdout strings
4119
	 * @param  array   $return_var    process exit code
4120
	 * @param  array   $error_output  stderr strings
4121
	 * @return int     exit code
4122
	 * @author Alexey Sukhotin
4123
	 **/
4124
	protected function procExec($command , array &$output = null, &$return_var = -1, array &$error_output = null) {
4125
4126
		$descriptorspec = array(
4127
			0 => array("pipe", "r"),  // stdin
4128
			1 => array("pipe", "w"),  // stdout
4129
			2 => array("pipe", "w")   // stderr
4130
		);
4131
4132
		$process = proc_open($command, $descriptorspec, $pipes, null, null);
4133
4134
		if (is_resource($process)) {
4135
4136
			fclose($pipes[0]);
4137
4138
			$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...
4139
			$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...
4140
4141
			$output = stream_get_contents($pipes[1]);
4142
			$error_output = stream_get_contents($pipes[2]);
4143
4144
			fclose($pipes[1]);
4145
			fclose($pipes[2]);
4146
			$return_var = proc_close($process);
4147
4148
4149
		}
4150
		
4151
		return $return_var;
4152
		
4153
	}
4154
4155
	/**
4156
	 * Remove thumbnail, also remove recursively if stat is directory
4157
	 *
4158
	 * @param  string  $stat  file stat
4159
	 * @return void
4160
	 * @author Dmitry (dio) Levashov
4161
	 * @author Naoki Sawada
4162
	 * @author Troex Nevelin
4163
	 **/
4164
	protected function rmTmb($stat) {
4165
		if ($stat['mime'] === 'directory') {
4166
			foreach ($this->scandirCE($this->decode($stat['hash'])) as $p) {
4167
				@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...
4168
				$name = $this->basenameCE($p);
4169
				$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...
4170
			}
4171
		} else if (!empty($stat['tmb']) && $stat['tmb'] != "1") {
4172
			$tmb = $this->tmbPath.DIRECTORY_SEPARATOR.$stat['tmb'];
4173
			file_exists($tmb) && @unlink($tmb);
4174
			clearstatcache();
4175
		}
4176
	}
4177
	
4178
	/**
4179
	 * Create an gd image according to the specified mime type
4180
	 *
4181
	 * @param string $path image file
4182
	 * @param string $mime
4183
	 * @return gd image resource identifier
4184
	 */
4185
	protected function gdImageCreate($path,$mime){
4186
		switch($mime){
4187
			case 'image/jpeg':
4188
			return imagecreatefromjpeg($path);
4189
4190
			case 'image/png':
4191
			return imagecreatefrompng($path);
4192
4193
			case 'image/gif':
4194
			return imagecreatefromgif($path);
4195
4196
			case 'image/xbm':
4197
			return imagecreatefromxbm($path);
4198
		}
4199
		return false;
4200
	}
4201
4202
	/**
4203
	 * Output gd image to file
4204
	 *
4205
	 * @param resource $image gd image resource
4206
	 * @param string $filename The path to save the file to.
4207
	 * @param string $destformat The Image type to use for $filename
4208
	 * @param string $mime The original image mime type
4209
	 * @param int    $jpgQuality  JEPG quality (1-100)
4210
	 */
4211
	protected function gdImage($image, $filename, $destformat, $mime, $jpgQuality = null ){
4212
4213
		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...
4214
			$jpgQuality = $this->options['jpgQuality'];
4215
		}
4216 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...
4217
			return imagejpeg($image, $filename, $jpgQuality);
4218
		}
4219
4220 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...
4221
			return imagegif($image, $filename, 7);
4222
		}
4223
4224
		return imagepng($image, $filename, 7);
4225
	}
4226
4227
	/**
4228
	 * Output imagick image to file
4229
	 *
4230
	 * @param resource $img imagick image resource
4231
	 * @param string $filename The path to save the file to.
4232
	 * @param string $destformat The Image type to use for $filename
4233
	 * @param int    $jpgQuality  JEPG quality (1-100)
4234
	 */
4235
	protected function imagickImage($img, $filename, $destformat, $jpgQuality = null ){
4236
4237
		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...
4238
			$jpgQuality = $this->options['jpgQuality'];
4239
		}
4240
		
4241
		try {
4242
			if ($destformat) {
4243
				if ($destformat === 'gif') {
4244
					$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...
4245
				} else if ($destformat === 'png') {
4246
					$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...
4247
				} else if ($destformat === 'jpg') {
4248
					$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...
4249
				}
4250
			}
4251 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...
4252
				$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...
4253
				$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...
4254
				try {
4255
					$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...
4256
				} catch (ImagickException $e) {
4257
					$orientation = 0;
4258
				}
4259
				$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...
4260
				if ($orientation) {
4261
					$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...
4262
				}
4263
			}
4264
			$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...
4265
		} catch (Exception $e) {
4266
			$result = false;
4267
		}
4268
		
4269
		return $result;
4270
		
4271
		
4272
		
4273 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...
4274
			return imagejpeg($image, $filename, $jpgQuality);
4275
		}
4276
4277 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...
4278
			return imagegif($image, $filename, 7);
4279
		}
4280
4281
		return imagepng($image, $filename, 7);
4282
	}
4283
4284
	/**
4285
	 * Assign the proper background to a gd image
4286
	 *
4287
	 * @param resource $image gd image resource
4288
	 * @param string $bgcolor background color in #rrggbb format
4289
	 */
4290
	protected function gdImageBackground($image, $bgcolor){
4291
4292
		if( $bgcolor == 'transparent' ){
4293
			imagesavealpha($image,true);
4294
			$bgcolor1 = imagecolorallocatealpha($image, 255, 255, 255, 127);
4295
4296
		}else{
4297
			list($r, $g, $b) = sscanf($bgcolor, "#%02x%02x%02x");
4298
			$bgcolor1 = imagecolorallocate($image, $r, $g, $b);
4299
		}
4300
4301
		imagefill($image, 0, 0, $bgcolor1);
4302
	}
4303
4304
	/*********************** misc *************************/
4305
	
4306
	/**
4307
	 * Return smart formatted date
4308
	 *
4309
	 * @param  int     $ts  file timestamp
4310
	 * @return string
4311
	 * @author Dmitry (dio) Levashov
4312
	 **/
4313
	// 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...
4314
	// 	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...
4315
	// 		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...
4316
	// 	}
4317
	// 	
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...
4318
	// 	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...
4319
	// 		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...
4320
	// 	} 
4321
	// 	
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...
4322
	// 	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...
4323
	// }
4324
4325
	/**
4326
	* Find position of first occurrence of string in a string with multibyte support
4327
	*
4328
	* @param  string  $haystack  The string being checked.
4329
	* @param  string  $needle    The string to find in haystack.
4330
	* @param  int     $offset    The search offset. If it is not specified, 0 is used.
4331
	* @return int|bool
4332
	* @author Alexey Sukhotin
4333
	**/
4334
	protected function stripos($haystack , $needle , $offset = 0) {
4335
		if (function_exists('mb_stripos')) {
4336
			return mb_stripos($haystack , $needle , $offset, 'UTF-8');
4337
		} else if (function_exists('mb_strtolower') && function_exists('mb_strpos')) {
4338
			return mb_strpos(mb_strtolower($haystack, 'UTF-8'), mb_strtolower($needle, 'UTF-8'), $offset);
4339
		} 
4340
		return stripos($haystack , $needle , $offset);
4341
	}
4342
4343
	/**
4344
	 * Get server side available archivers
4345
	 * 
4346
	 * @param bool $use_cache
4347
	 * @return array
4348
	 */
4349
	protected function getArchivers($use_cache = true) {
4350
4351
		$sessionKey = 'ARCHIVERS_CACHE';
4352
		if ($use_cache && isset($this->sessionCache[$sessionKey]) && is_array($this->sessionCache[$sessionKey])) {
4353
			return $this->sessionCache[$sessionKey];
4354
		}
4355
		
4356
		$arcs = array(
4357
			'create'  => array(),
4358
			'extract' => array()
4359
		);
4360
		
4361
		if (function_exists('proc_open')) {
4362
		
4363
			$this->procExec('tar --version', $o, $ctar);
4364
			
4365
			if ($ctar == 0) {
4366
				$arcs['create']['application/x-tar']  = array('cmd' => 'tar', 'argc' => '-cf', 'ext' => 'tar');
4367
				$arcs['extract']['application/x-tar'] = array('cmd' => 'tar', 'argc' => '-xf', 'ext' => 'tar');
4368
				unset($o);
4369
				$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...
4370 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...
4371
					$arcs['create']['application/x-gzip']  = array('cmd' => 'tar', 'argc' => '-czf', 'ext' => 'tgz');
4372
					$arcs['extract']['application/x-gzip'] = array('cmd' => 'tar', 'argc' => '-xzf', 'ext' => 'tgz');
4373
				}
4374
				unset($o);
4375
				$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...
4376 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...
4377
					$arcs['create']['application/x-bzip2']  = array('cmd' => 'tar', 'argc' => '-cjf', 'ext' => 'tbz');
4378
					$arcs['extract']['application/x-bzip2'] = array('cmd' => 'tar', 'argc' => '-xjf', 'ext' => 'tbz');
4379
				}
4380
				unset($o);
4381
				$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...
4382 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...
4383
					$arcs['create']['application/x-xz']  = array('cmd' => 'tar', 'argc' => '-cJf', 'ext' => 'xz');
4384
					$arcs['extract']['application/x-xz'] = array('cmd' => 'tar', 'argc' => '-xJf', 'ext' => 'xz');
4385
				}
4386
			}
4387
			unset($o);
4388
			$this->procExec('zip -v', $o, $c);
4389
			if ($c == 0) {
4390
				$arcs['create']['application/zip']  = array('cmd' => 'zip', 'argc' => '-r9', 'ext' => 'zip');
4391
			}
4392
			unset($o);
4393
			$this->procExec('unzip --help', $o, $c);
4394
			if ($c == 0) {
4395
				$arcs['extract']['application/zip'] = array('cmd' => 'unzip', 'argc' => '',  'ext' => 'zip');
4396
			}
4397
			unset($o);
4398
			$this->procExec('rar --version', $o, $c);
4399
			if ($c == 0 || $c == 7) {
4400
				$arcs['create']['application/x-rar']  = array('cmd' => 'rar', 'argc' => 'a -inul', 'ext' => 'rar');
4401
				$arcs['extract']['application/x-rar'] = array('cmd' => 'rar', 'argc' => 'x -y',    'ext' => 'rar');
4402
			} else {
4403
				unset($o);
4404
				$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...
4405
				if ($c==0 || $c == 7) {
4406
					$arcs['extract']['application/x-rar'] = array('cmd' => 'unrar', 'argc' => 'x -y', 'ext' => 'rar');
4407
				}
4408
			}
4409
			unset($o);
4410
			$this->procExec('7za --help', $o, $c);
4411
			if ($c == 0) {
4412
				$arcs['create']['application/x-7z-compressed']  = array('cmd' => '7za', 'argc' => 'a', 'ext' => '7z');
4413
				$arcs['extract']['application/x-7z-compressed'] = array('cmd' => '7za', 'argc' => 'x -y', 'ext' => '7z');
4414
				
4415
				if (empty($arcs['create']['application/zip'])) {
4416
					$arcs['create']['application/zip'] = array('cmd' => '7za', 'argc' => 'a -tzip', 'ext' => 'zip');
4417
				}
4418 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...
4419
					$arcs['extract']['application/zip'] = array('cmd' => '7za', 'argc' => 'x -tzip -y', 'ext' => 'zip');
4420
				}
4421
				if (empty($arcs['create']['application/x-tar'])) {
4422
					$arcs['create']['application/x-tar'] = array('cmd' => '7za', 'argc' => 'a -ttar', 'ext' => 'tar');
4423
				}
4424 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...
4425
					$arcs['extract']['application/x-tar'] = array('cmd' => '7za', 'argc' => 'x -ttar -y', 'ext' => 'tar');
4426
				}
4427
			} else if (substr(PHP_OS,0,3) === 'WIN') {
4428
				// check `7z` for Windows server.
4429
				unset($o);
4430
				$this->procExec('7z', $o, $c);
4431
				if ($c == 0) {
4432
					$arcs['create']['application/x-7z-compressed']  = array('cmd' => '7z', 'argc' => 'a', 'ext' => '7z');
4433
					$arcs['extract']['application/x-7z-compressed'] = array('cmd' => '7z', 'argc' => 'x -y', 'ext' => '7z');
4434
					
4435
					if (empty($arcs['create']['application/zip'])) {
4436
						$arcs['create']['application/zip'] = array('cmd' => '7z', 'argc' => 'a -tzip', 'ext' => 'zip');
4437
					}
4438
					if (empty($arcs['extract']['application/zip'])) {
4439
						$arcs['extract']['application/zip'] = array('cmd' => '7z', 'argc' => 'x -tzip -y', 'ext' => 'zip');
4440
					}
4441
					if (empty($arcs['create']['application/x-tar'])) {
4442
						$arcs['create']['application/x-tar'] = array('cmd' => '7z', 'argc' => 'a -ttar', 'ext' => 'tar');
4443
					}
4444
					if (empty($arcs['extract']['application/x-tar'])) {
4445
						$arcs['extract']['application/x-tar'] = array('cmd' => '7z', 'argc' => 'x -ttar -y', 'ext' => 'tar');
4446
					}
4447
				}
4448
			}
4449
		
4450
		}
4451
		
4452
		// Use PHP ZipArchive Class
4453
		if (class_exists('ZipArchive', false)) {
4454
			if (empty($arcs['create']['application/zip'])) {
4455
				$arcs['create']['application/zip']  = array('cmd' => 'phpfunction', 'argc' => 'self::zipArchiveZip', 'ext' => 'zip');
4456
			}
4457 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...
4458
				$arcs['extract']['application/zip'] = array('cmd' => 'phpfunction', 'argc' => 'self::zipArchiveUnzip', 'ext' => 'zip');
4459
			}
4460
		}
4461
		
4462
		$this->sessionCache[$sessionKey] = $arcs;
4463
		return $arcs;
4464
	}
4465
4466
	/**
4467
	 * Resolve relative / (Unix-like)absolute path
4468
	 * 
4469
	 * @param string $path  target path
4470
	 * @param string $base  base path
4471
	 * @return string
4472
	 */
4473
	protected function getFullPath($path, $base) {
4474
		$separator = $this->separator;
4475
		$systemroot = $this->systemRoot;
4476
4477
		$sepquoted = preg_quote($separator, '#');
4478
4479
		// normalize `/../`
4480
		$normreg = '#('.$sepquoted.')[^'.$sepquoted.']+'.$sepquoted.'\.\.'.$sepquoted.'#';
4481
		while(preg_match($normreg, $path)) {
4482
			$path = preg_replace($normreg, '$1', $path);
4483
		}
4484
		
4485
		// 'Here'
4486
		if ($path === '' || $path === '.' . $separator) return $base;
4487
		
4488
		// Absolute path
4489
		if ($path[0] === $separator || strpos($path, $systemroot) === 0) {
4490
			return $path;
4491
		}
4492
		
4493
		$preg_separator = '#' . $sepquoted . '#';
4494
		
4495
		// Relative path from 'Here'
4496
		if (substr($path, 0, 2) === '.' . $separator || $path[0] !== '.' || substr($path, 0, 3) !== '..' . $separator) {
4497
			$arrn = preg_split($preg_separator, $path, -1, PREG_SPLIT_NO_EMPTY);
4498
			if ($arrn[0] !== '.') {
4499
				array_unshift($arrn, '.');
4500
			}
4501
			$arrn[0] = $base;
4502
			return join($separator, $arrn);
4503
		}
4504
		
4505
		// Relative path from dirname()
4506
		if (substr($path, 0, 3) === '../') {
4507
			$arrn = preg_split($preg_separator, $path, -1, PREG_SPLIT_NO_EMPTY);
4508
			$arrp = preg_split($preg_separator, $base, -1, PREG_SPLIT_NO_EMPTY);
4509
		
4510
			while (! empty($arrn) && $arrn[0] === '..') {
4511
				array_shift($arrn);
4512
				array_pop($arrp);
4513
			}
4514
			$path = ! empty($arrp) ? $systemroot . join($separator, array_merge($arrp, $arrn)) :
4515
				(! empty($arrn) ? $systemroot . join($separator, $arrn) : $systemroot);
4516
		}
4517
		
4518
		return $path;
4519
	}
4520
4521
	/**
4522
	 * Remove directory recursive on local file system
4523
	 *
4524
	 * @param string $dir Target dirctory path
4525
	 * @return boolean
4526
	 * @author Naoki Sawada
4527
	 */
4528
	public function rmdirRecursive($dir) {
4529
		if (!is_link($dir) && is_dir($dir)) {
4530
			@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...
4531
			foreach (array_diff(scandir($dir), array('.', '..')) as $file) {
4532
				@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...
4533
				$path = $dir . DIRECTORY_SEPARATOR . $file;
4534
				if (!is_link($dir) && is_dir($path)) {
4535
					$this->rmdirRecursive($path);
4536
				} else {
4537
					@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...
4538
					@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...
4539
				}
4540
			}
4541
			return @rmdir($dir);
4542
		} else if (is_file($dir) || is_link($dir)) {
4543
			@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...
4544
			return @unlink($dir);
4545
		}
4546
		return false;
4547
	}
4548
4549
	/**
4550
	 * Create archive and return its path
4551
	 *
4552
	 * @param  string  $dir    target dir
4553
	 * @param  array   $files  files names list
4554
	 * @param  string  $name   archive name
4555
	 * @param  array   $arc    archiver options
4556
	 * @return string|bool
4557
	 * @author Dmitry (dio) Levashov, 
4558
	 * @author Alexey Sukhotin
4559
	 * @author Naoki Sawada
4560
	 **/
4561
	protected function makeArchive($dir, $files, $name, $arc) {
4562
		if ($arc['cmd'] === 'phpfunction') {
4563 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...
4564
				call_user_func_array($arc['argc'], array($dir, $files, $name));
4565
			}
4566
		} else {
4567
			$cwd = getcwd();
4568
			chdir($dir);
4569
			
4570
			$files = array_map('escapeshellarg', $files);
4571
			
4572
			$cmd = $arc['cmd'].' '.$arc['argc'].' '.escapeshellarg($name).' '.implode(' ', $files);
4573
			$this->procExec($cmd, $o, $c);
4574
			chdir($cwd);
4575
		}
4576
		$path = $dir.DIRECTORY_SEPARATOR.$name;
4577
		return file_exists($path) ? $path : false;
4578
	}
4579
	
4580
	/**
4581
	 * Unpack archive
4582
	 *
4583
	 * @param  string  $path   archive path
4584
	 * @param  array   $arc    archiver command and arguments (same as in $this->archivers)
4585
	 * @param  bool    $remove remove archive ( unlink($path) )
4586
	 * @return void
4587
	 * @author Dmitry (dio) Levashov
4588
	 * @author Alexey Sukhotin
4589
	 * @author Naoki Sawada
4590
	 **/
4591
	protected function unpackArchive($path, $arc, $remove = true) {
4592
		$dir = dirname($path);
4593
		if ($arc['cmd'] === 'phpfunction') {
4594 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...
4595
				call_user_func_array($arc['argc'], array($path, $dir));
4596
			}
4597
		} else {
4598
			$cwd = getcwd();
4599
			chdir($dir);
4600
			$cmd = $arc['cmd'].' '.$arc['argc'].' '.escapeshellarg(basename($path));
4601
			$this->procExec($cmd, $o, $c);
4602
			chdir($cwd);
4603
		}
4604
		$remove && unlink($path);
4605
	}
4606
	
4607
	/**
4608
	 * Create Zip archive using PHP class ZipArchive
4609
	 * 
4610
	 * @param  string        $dir      target dir
4611
	 * @param  array         $files    files names list
4612
	 * @param  string|object $zipPath  Zip archive name
4613
	 * @return void
4614
	 * @author Naoki Sawada
4615
	 */
4616
	protected static function zipArchiveZip($dir, $files, $zipPath) {
4617
		try {
4618
			if ($start = is_string($zipPath)) {
4619
				$zip = new ZipArchive();
4620
				if ($zip->open($dir . DIRECTORY_SEPARATOR . $zipPath, ZipArchive::CREATE) !== true) {
4621
					$zip = false;
4622
				}
4623
			} else {
4624
				$zip = $zipPath;
4625
			}
4626
			if ($zip) {
4627
				foreach($files as $file) {
4628
					$path = $dir . DIRECTORY_SEPARATOR . $file;
4629
					if (is_dir($path)) {
4630
						$zip->addEmptyDir($file);
4631
						$_files = array();
4632
						if ($handle = opendir($path)) {
4633
							while (false !== ($entry = readdir($handle))) {
4634
								if ($entry !== "." && $entry !== "..") {
4635
									$_files[] = $file . DIRECTORY_SEPARATOR . $entry;
4636
								}
4637
							}
4638
							closedir($handle);
4639
						}
4640
						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...
4641
							self::zipArchiveZip($dir, $_files, $zip);
4642
						}
4643
					} else {
4644
						$zip->addFile($path, $file);
4645
					}
4646
				}
4647
				$start && $zip->close();
4648
			}
4649
		} catch (Exception $e) {
4650
			return false;
4651
		}
4652
		return true;
4653
	}
4654
	
4655
	/**
4656
	 * Unpack Zip archive using PHP class ZipArchive
4657
	 * 
4658
	 * @param  string $zipPath  Zip archive name
4659
	 * @param  string $toDir    Extract to path
4660
	 * @return bool
4661
	 * @author Naoki Sawada
4662
	 */
4663
	protected static function zipArchiveUnzip($zipPath, $toDir) {
4664
		try {
4665
			$zip = new ZipArchive();
4666
			if ($zip->open($zipPath) === true) {
4667
				$zip->extractTo($toDir);
4668
				$zip->close();
4669
			}
4670
		} catch (Exception $e) {
4671
			return false;
4672
		}
4673
		return true;
4674
	}
4675
	
4676
	/**==================================* abstract methods *====================================**/
4677
	
4678
	/*********************** paths/urls *************************/
4679
	
4680
	/**
4681
	 * Return parent directory path
4682
	 *
4683
	 * @param  string  $path  file path
4684
	 * @return string
4685
	 * @author Dmitry (dio) Levashov
4686
	 **/
4687
	abstract protected function _dirname($path);
4688
4689
	/**
4690
	 * Return file name
4691
	 *
4692
	 * @param  string  $path  file path
4693
	 * @return string
4694
	 * @author Dmitry (dio) Levashov
4695
	 **/
4696
	abstract protected function _basename($path);
4697
4698
	/**
4699
	 * Join dir name and file name and return full path.
4700
	 * Some drivers (db) use int as path - so we give to concat path to driver itself
4701
	 *
4702
	 * @param  string  $dir   dir path
4703
	 * @param  string  $name  file name
4704
	 * @return string
4705
	 * @author Dmitry (dio) Levashov
4706
	 **/
4707
	abstract protected function _joinPath($dir, $name);
4708
4709
	/**
4710
	 * Return normalized path 
4711
	 *
4712
	 * @param  string  $path  file path
4713
	 * @return string
4714
	 * @author Dmitry (dio) Levashov
4715
	 **/
4716
	abstract protected function _normpath($path);
4717
4718
	/**
4719
	 * Return file path related to root dir
4720
	 *
4721
	 * @param  string  $path  file path
4722
	 * @return string
4723
	 * @author Dmitry (dio) Levashov
4724
	 **/
4725
	abstract protected function _relpath($path);
4726
	
4727
	/**
4728
	 * Convert path related to root dir into real path
4729
	 *
4730
	 * @param  string  $path  rel file path
4731
	 * @return string
4732
	 * @author Dmitry (dio) Levashov
4733
	 **/
4734
	abstract protected function _abspath($path);
4735
	
4736
	/**
4737
	 * Return fake path started from root dir.
4738
	 * Required to show path on client side.
4739
	 *
4740
	 * @param  string  $path  file path
4741
	 * @return string
4742
	 * @author Dmitry (dio) Levashov
4743
	 **/
4744
	abstract protected function _path($path);
4745
	
4746
	/**
4747
	 * Return true if $path is children of $parent
4748
	 *
4749
	 * @param  string  $path    path to check
4750
	 * @param  string  $parent  parent path
4751
	 * @return bool
4752
	 * @author Dmitry (dio) Levashov
4753
	 **/
4754
	abstract protected function _inpath($path, $parent);
4755
	
4756
	/**
4757
	 * Return stat for given path.
4758
	 * Stat contains following fields:
4759
	 * - (int)    size    file size in b. required
4760
	 * - (int)    ts      file modification time in unix time. required
4761
	 * - (string) mime    mimetype. required for folders, others - optionally
4762
	 * - (bool)   read    read permissions. required
4763
	 * - (bool)   write   write permissions. required
4764
	 * - (bool)   locked  is object locked. optionally
4765
	 * - (bool)   hidden  is object hidden. optionally
4766
	 * - (string) alias   for symlinks - link target path relative to root path. optionally
4767
	 * - (string) target  for symlinks - link target path. optionally
4768
	 *
4769
	 * If file does not exists - returns empty array or false.
4770
	 *
4771
	 * @param  string  $path    file path 
4772
	 * @return array|false
4773
	 * @author Dmitry (dio) Levashov
4774
	 **/
4775
	abstract protected function _stat($path);
4776
	
4777
4778
	/***************** file stat ********************/
4779
4780
		
4781
	/**
4782
	 * Return true if path is dir and has at least one childs directory
4783
	 *
4784
	 * @param  string  $path  dir path
4785
	 * @return bool
4786
	 * @author Dmitry (dio) Levashov
4787
	 **/
4788
	abstract protected function _subdirs($path);
4789
	
4790
	/**
4791
	 * Return object width and height
4792
	 * Ususaly used for images, but can be realize for video etc...
4793
	 *
4794
	 * @param  string  $path  file path
4795
	 * @param  string  $mime  file mime type
4796
	 * @return string
4797
	 * @author Dmitry (dio) Levashov
4798
	 **/
4799
	abstract protected function _dimensions($path, $mime);
4800
	
4801
	/******************** file/dir content *********************/
4802
4803
	/**
4804
	 * Return files list in directory
4805
	 *
4806
	 * @param  string  $path  dir path
4807
	 * @return array
4808
	 * @author Dmitry (dio) Levashov
4809
	 **/
4810
	abstract protected function _scandir($path);
4811
	
4812
	/**
4813
	 * Open file and return file pointer
4814
	 *
4815
	 * @param  string $path file path
4816
	 * @param  string $mode open mode
4817
	 * @return resource|false
4818
	 * @author Dmitry (dio) Levashov
4819
	 **/
4820
	abstract protected function _fopen($path, $mode="rb");
4821
	
4822
	/**
4823
	 * Close opened file
4824
	 * 
4825
	 * @param  resource  $fp    file pointer
4826
	 * @param  string    $path  file path
4827
	 * @return bool
4828
	 * @author Dmitry (dio) Levashov
4829
	 **/
4830
	abstract protected function _fclose($fp, $path='');
4831
	
4832
	/********************  file/dir manipulations *************************/
4833
	
4834
	/**
4835
	 * Create dir and return created dir path or false on failed
4836
	 *
4837
	 * @param  string  $path  parent dir path
4838
	 * @param string  $name  new directory name
4839
	 * @return string|bool
4840
	 * @author Dmitry (dio) Levashov
4841
	 **/
4842
	abstract protected function _mkdir($path, $name);
4843
	
4844
	/**
4845
	 * Create file and return it's path or false on failed
4846
	 *
4847
	 * @param  string  $path  parent dir path
4848
	 * @param string  $name  new file name
4849
	 * @return string|bool
4850
	 * @author Dmitry (dio) Levashov
4851
	 **/
4852
	abstract protected function _mkfile($path, $name);
4853
	
4854
	/**
4855
	 * Create symlink
4856
	 *
4857
	 * @param  string  $source     file to link to
4858
	 * @param  string  $targetDir  folder to create link in
4859
	 * @param  string  $name       symlink name
4860
	 * @return bool
4861
	 * @author Dmitry (dio) Levashov
4862
	 **/
4863
	abstract protected function _symlink($source, $targetDir, $name);
4864
	
4865
	/**
4866
	 * Copy file into another file (only inside one volume)
4867
	 *
4868
	 * @param  string  $source  source file path
4869
	 * @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...
4870
	 * @param  string  $name    file name
4871
	 * @return bool
4872
	 * @author Dmitry (dio) Levashov
4873
	 **/
4874
	abstract protected function _copy($source, $targetDir, $name);
4875
	
4876
	/**
4877
	 * Move file into another parent dir.
4878
	 * Return new file path or false.
4879
	 *
4880
	 * @param  string  $source  source file path
4881
	 * @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...
4882
	 * @param  string  $name    file name
4883
	 * @return string|bool
4884
	 * @author Dmitry (dio) Levashov
4885
	 **/
4886
	abstract protected function _move($source, $targetDir, $name);
4887
	
4888
	/**
4889
	 * Remove file
4890
	 *
4891
	 * @param  string  $path  file path
4892
	 * @return bool
4893
	 * @author Dmitry (dio) Levashov
4894
	 **/
4895
	abstract protected function _unlink($path);
4896
4897
	/**
4898
	 * Remove dir
4899
	 *
4900
	 * @param  string  $path  dir path
4901
	 * @return bool
4902
	 * @author Dmitry (dio) Levashov
4903
	 **/
4904
	abstract protected function _rmdir($path);
4905
4906
	/**
4907
	 * Create new file and write into it from file pointer.
4908
	 * Return new file path or false on error.
4909
	 *
4910
	 * @param  resource  $fp   file pointer
4911
	 * @param  string    $dir  target dir path
4912
	 * @param  string    $name file name
4913
	 * @param  array     $stat file stat (required by some virtual fs)
4914
	 * @return bool|string
4915
	 * @author Dmitry (dio) Levashov
4916
	 **/
4917
	abstract protected function _save($fp, $dir, $name, $stat);
4918
	
4919
	/**
4920
	 * Get file contents
4921
	 *
4922
	 * @param  string  $path  file path
4923
	 * @return string|false
4924
	 * @author Dmitry (dio) Levashov
4925
	 **/
4926
	abstract protected function _getContents($path);
4927
	
4928
	/**
4929
	 * Write a string to a file
4930
	 *
4931
	 * @param  string  $path     file path
4932
	 * @param  string  $content  new file content
4933
	 * @return bool
4934
	 * @author Dmitry (dio) Levashov
4935
	 **/
4936
	abstract protected function _filePutContents($path, $content);
4937
4938
	/**
4939
	 * Extract files from archive
4940
	 *
4941
	 * @param  string  $path file path
4942
	 * @param  array   $arc  archiver options
4943
	 * @return bool
4944
	 * @author Dmitry (dio) Levashov, 
4945
	 * @author Alexey Sukhotin
4946
	 **/
4947
	abstract protected function _extract($path, $arc);
4948
4949
	/**
4950
	 * Create archive and return its path
4951
	 *
4952
	 * @param  string  $dir    target dir
4953
	 * @param  array   $files  files names list
4954
	 * @param  string  $name   archive name
4955
	 * @param  array   $arc    archiver options
4956
	 * @return string|bool
4957
	 * @author Dmitry (dio) Levashov, 
4958
	 * @author Alexey Sukhotin
4959
	 **/
4960
	abstract protected function _archive($dir, $files, $name, $arc);
4961
4962
	/**
4963
	 * Detect available archivers
4964
	 *
4965
	 * @return void
4966
	 * @author Dmitry (dio) Levashov, 
4967
	 * @author Alexey Sukhotin
4968
	 **/
4969
	abstract protected function _checkArchivers();
4970
4971
	/**
4972
	 * Change file mode (chmod)
4973
	 *
4974
	 * @param  string  $path  file path
4975
	 * @param  string  $mode  octal string such as '0755'
4976
	 * @return bool
4977
	 * @author David Bartle,
4978
	 **/
4979
	abstract protected function _chmod($path, $mode);
4980
4981
	
4982
} // END class
4983