Completed
Push — 2.x ( 53abae...3d75fb )
by Naoki
13:12 queued 24s
created

elFinderVolumeDriver::imagickImage()   D

Complexity

Conditions 16
Paths 186

Size

Total Lines 48
Code Lines 30

Duplication

Lines 19
Ratio 39.58 %
Metric Value
dl 19
loc 48
rs 4.6845
cc 16
eloc 30
nc 186
nop 4

How to fix   Complexity   

Long Method

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

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

Commonly applied refactorings include:

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

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

namespace YourVendor;

class YourClass { }

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

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

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

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

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

Loading history...
974
		// echo $this->options['uploadMaxSize'];
0 ignored issues
show
Unused Code Comprehensibility introduced by
70% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

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

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

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

Loading history...
2981
				if ($stat['mime'] === 'directory') {
2982
					$this->subdirsCache[$path] = true;
2983
				}
2984
				$this->dirsCache[$path][] = $p;
2985
			}
2986
		}
2987
	}
2988
	
2989
	/**
2990
	 * Clean cache
2991
	 *
2992
	 * @return void
2993
	 * @author Dmitry (dio) Levashov
2994
	 **/
2995
	protected function clearcache() {
2996
		$this->cache = $this->dirsCache = array();
2997
		unset($this->sessionCache['rootstat'][md5($this->root)]);
2998
	}
2999
	
3000
	/**
3001
	 * Return file mimetype
3002
	 *
3003
	 * @param  string  $path  file path
3004
	 * @return string
3005
	 * @author Dmitry (dio) Levashov
3006
	 **/
3007
	protected function mimetype($path, $name = '') {
3008
		$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...
3009
		
3010
		if ($name === '') {
3011
			$name = $path;
3012
		}
3013
		$ext = (false === $pos = strrpos($name, '.')) ? '' : substr($name, $pos + 1);
3014
		if ($this->mimeDetect == 'finfo') {
3015
			if ($type = @finfo_file($this->finfo, $path)) {
3016
				if ($ext && preg_match('~^application/(?:octet-stream|(?:x-)?zip)~', $type)) {
3017
					if (isset(elFinderVolumeDriver::$mimetypes[$ext])) $type = elFinderVolumeDriver::$mimetypes[$ext];
3018
				} else if ($ext === 'js' && preg_match('~^text/~', $type)) {
3019
					$type = 'text/javascript';
3020
				}
3021
			} else {
3022
				$type = 'unknown';
3023
			}
3024
		} elseif ($this->mimeDetect == 'mime_content_type') {
3025
			$type = mime_content_type($path);
3026
		} else {
3027
			$type = elFinderVolumeDriver::mimetypeInternalDetect($path);
3028
		}
3029
		
3030
		$type = explode(';', $type);
3031
		$type = trim($type[0]);
3032
3033 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...
3034
			// finfo return this mime for empty files
3035
			$type = 'text/plain';
3036
		} elseif ($type == 'application/x-zip') {
3037
			// http://elrte.org/redmine/issues/163
3038
			$type = 'application/zip';
3039
		}
3040
		
3041
		// mime type normalization
3042
		$_checkKey = strtolower($ext.':'.$type);
3043
		if (isset($this->options['mimeMap'][$_checkKey])) {
3044
			$type = $this->options['mimeMap'][$_checkKey];
3045
		}
3046
		
3047
		return $type == 'unknown' && $this->mimeDetect != 'internal'
3048
			? elFinderVolumeDriver::mimetypeInternalDetect($path)
3049
			: $type;
3050
		
3051
	}
3052
	
3053
	/**
3054
	 * Detect file mimetype using "internal" method
3055
	 *
3056
	 * @param  string  $path  file path
3057
	 * @return string
3058
	 * @author Dmitry (dio) Levashov
3059
	 **/
3060
	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...
3061
		// load default MIME table file "mime.types"
3062
		if (!elFinderVolumeDriver::$mimetypesLoaded) {
3063
			elFinderVolumeDriver::$mimetypesLoaded = true;
3064
			$file = dirname(__FILE__).DIRECTORY_SEPARATOR.'mime.types';
3065 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...
3066
				$mimecf = file($file);
3067
				foreach ($mimecf as $line_num => $line) {
3068
					if (!preg_match('/^\s*#/', $line)) {
3069
						$mime = preg_split('/\s+/', $line, -1, PREG_SPLIT_NO_EMPTY);
3070
						for ($i = 1, $size = count($mime); $i < $size ; $i++) {
3071
							if (!isset(elFinderVolumeDriver::$mimetypes[$mime[$i]])) {
3072
								elFinderVolumeDriver::$mimetypes[$mime[$i]] = $mime[0];
3073
							}
3074
						}
3075
					}
3076
				}
3077
			}
3078
		}
3079
		$pinfo = pathinfo($path); 
3080
		$ext   = isset($pinfo['extension']) ? strtolower($pinfo['extension']) : '';
3081
		return isset(elFinderVolumeDriver::$mimetypes[$ext]) ? elFinderVolumeDriver::$mimetypes[$ext] : 'unknown';
3082
	}
3083
	
3084
	/**
3085
	 * Return file/total directory size
3086
	 *
3087
	 * @param  string  $path  file path
3088
	 * @return int
3089
	 * @author Dmitry (dio) Levashov
3090
	 **/
3091
	protected function countSize($path) {
3092
		$stat = $this->stat($path);
3093
3094
		if (empty($stat) || !$stat['read'] || !empty($stat['hidden'])) {
3095
			return 'unknown';
3096
		}
3097
		
3098
		if ($stat['mime'] != 'directory') {
3099
			return $stat['size'];
3100
		}
3101
		
3102
		$subdirs = $this->options['checkSubfolders'];
3103
		$this->options['checkSubfolders'] = true;
3104
		$result = 0;
3105
		foreach ($this->getScandir($path) as $stat) {
3106
			$size = $stat['mime'] == 'directory' && $stat['read'] 
3107
				? $this->countSize($this->joinPathCE($path, $stat['name'])) 
3108
				: (isset($stat['size']) ? intval($stat['size']) : 0);
3109
			if ($size > 0) {
3110
				$result += $size;
3111
			}
3112
		}
3113
		$this->options['checkSubfolders'] = $subdirs;
3114
		return $result;
3115
	}
3116
	
3117
	/**
3118
	 * Return true if all mimes is directory or files
3119
	 *
3120
	 * @param  string  $mime1  mimetype
3121
	 * @param  string  $mime2  mimetype
3122
	 * @return bool
3123
	 * @author Dmitry (dio) Levashov
3124
	 **/
3125
	protected function isSameType($mime1, $mime2) {
3126
		return ($mime1 == 'directory' && $mime1 == $mime2) || ($mime1 != 'directory' && $mime2 != 'directory');
3127
	}
3128
	
3129
	/**
3130
	 * If file has required attr == $val - return file path,
3131
	 * If dir has child with has required attr == $val - return child path
3132
	 *
3133
	 * @param  string   $path  file path
3134
	 * @param  string   $attr  attribute name
3135
	 * @param  bool     $val   attribute value
3136
	 * @return string|false
3137
	 * @author Dmitry (dio) Levashov
3138
	 **/
3139
	protected function closestByAttr($path, $attr, $val) {
3140
		$stat = $this->stat($path);
3141
		
3142
		if (empty($stat)) {
3143
			return false;
3144
		}
3145
		
3146
		$v = isset($stat[$attr]) ? $stat[$attr] : false;
3147
		
3148
		if ($v == $val) {
3149
			return $path;
3150
		}
3151
3152
		return $stat['mime'] == 'directory'
3153
			? $this->childsByAttr($path, $attr, $val) 
3154
			: false;
3155
	}
3156
	
3157
	/**
3158
	 * Return first found children with required attr == $val
3159
	 *
3160
	 * @param  string   $path  file path
3161
	 * @param  string   $attr  attribute name
3162
	 * @param  bool     $val   attribute value
3163
	 * @return string|false
3164
	 * @author Dmitry (dio) Levashov
3165
	 **/
3166
	protected function childsByAttr($path, $attr, $val) {
3167
		foreach ($this->scandirCE($path) as $p) {
3168
			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...
3169
				return $_p;
3170
			}
3171
		}
3172
		return false;
3173
	}
3174
	
3175
	/*****************  get content *******************/
3176
	
3177
	/**
3178
	 * Return required dir's files info.
3179
	 * If onlyMimes is set - return only dirs and files of required mimes
3180
	 *
3181
	 * @param  string  $path  dir path
3182
	 * @return array
3183
	 * @author Dmitry (dio) Levashov
3184
	 **/
3185
	protected function getScandir($path) {
3186
		$files = array();
3187
		
3188
		!isset($this->dirsCache[$path]) && $this->cacheDir($path);
3189
3190
		foreach ($this->dirsCache[$path] as $p) {
3191
			if (($stat = $this->stat($p)) && empty($stat['hidden'])) {
3192
				$files[] = $stat;
3193
			}
3194
		}
3195
3196
		return $files;
3197
	}
3198
	
3199
	
3200
	/**
3201
	 * Return subdirs tree
3202
	 *
3203
	 * @param  string  $path  parent dir path
3204
	 * @param  int     $deep  tree deep
3205
	 * @return array
3206
	 * @author Dmitry (dio) Levashov
3207
	 **/
3208
	protected function gettree($path, $deep, $exclude='') {
3209
		$dirs = array();
3210
		
3211
		!isset($this->dirsCache[$path]) && $this->cacheDir($path);
3212
3213
		foreach ($this->dirsCache[$path] as $p) {
3214
			$stat = $this->stat($p);
3215
			
3216
			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...
3217
				$dirs[] = $stat;
3218
				if ($deep > 0 && !empty($stat['dirs'])) {
3219
					$dirs = array_merge($dirs, $this->gettree($p, $deep-1));
3220
				}
3221
			}
3222
		}
3223
3224
		return $dirs;
3225
	}	
3226
		
3227
	/**
3228
	 * Recursive files search
3229
	 *
3230
	 * @param  string  $path   dir path
3231
	 * @param  string  $q      search string
3232
	 * @param  array   $mimes
3233
	 * @return array
3234
	 * @author Dmitry (dio) Levashov
3235
	 **/
3236
	protected function doSearch($path, $q, $mimes) {
3237
		$result = array();
3238
3239
		foreach($this->scandirCE($path) as $p) {
3240
			@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...
3241
			$stat = $this->stat($p);
3242
3243
			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...
3244
				continue;
3245
			}
3246
3247
			if (!empty($stat['hidden']) || !$this->mimeAccepted($stat['mime'], $mimes)) {
3248
				continue;
3249
			}
3250
			
3251
			$name = $stat['name'];
3252
3253
			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...
3254
				$stat['path'] = $this->path($stat['hash']);
3255
				if ($this->URL && !isset($stat['url'])) {
3256
					$path = str_replace($this->separator, '/', substr($p, strlen($this->root) + 1));
3257
					if ($this->encoding) {
3258
						$path = str_replace('%2F', '/', rawurlencode($this->convEncIn($path, true)));
3259
					}
3260
					$stat['url'] = $this->URL . $path;
3261
				}
3262
				
3263
				$result[] = $stat;
3264
			}
3265 View Code Duplication
			if ($stat['mime'] == 'directory' && $stat['read'] && !isset($stat['alias'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3266
				$result = array_merge($result, $this->doSearch($p, $q, $mimes));
3267
			}
3268
		}
3269
		
3270
		return $result;
3271
	}
3272
		
3273
	/**********************  manuipulations  ******************/
3274
		
3275
	/**
3276
	 * Copy file/recursive copy dir only in current volume.
3277
	 * Return new file path or false.
3278
	 *
3279
	 * @param  string  $src   source path
3280
	 * @param  string  $dst   destination dir path
3281
	 * @param  string  $name  new file name (optionaly)
3282
	 * @return string|false
3283
	 * @author Dmitry (dio) Levashov
3284
	 **/
3285
	protected function copy($src, $dst, $name) {
3286
		$srcStat = $this->stat($src);
3287
		$this->clearcache();
3288
		
3289
		if (!empty($srcStat['thash'])) {
3290
			$target = $this->decode($srcStat['thash']);
3291
			if (!$this->inpathCE($target, $this->root)) {
3292
				return $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']), elFinder::ERROR_MKOUTLINK);
3293
			}
3294
			$stat   = $this->stat($target);
3295
			$this->clearcache();
3296
			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...
3297
				? $this->joinPathCE($dst, $name)
3298
				: $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']));
3299
		} 
3300
		
3301
		if ($srcStat['mime'] == 'directory') {
3302
			$test = $this->stat($this->joinPathCE($dst, $name));
3303
			
3304 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...
3305
				return $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']));
3306
			}
3307
			
3308
			$dst = $this->joinPathCE($dst, $name);
3309
			
3310
			foreach ($this->getScandir($src) as $stat) {
3311
				if (empty($stat['hidden'])) {
3312
					$name = $stat['name'];
3313
					if (!$this->copy($this->joinPathCE($src, $name), $dst, $name)) {
3314
						$this->remove($dst, true); // fall back
3315
						return $this->setError($this->error, elFinder::ERROR_COPY, $this->_path($src));
3316
					}
3317
				}
3318
			}
3319
			$this->clearcache();
3320
			return $dst;
3321
		} 
3322
3323
		return $this->convEncOut($this->_copy($this->convEncIn($src), $this->convEncIn($dst), $this->convEncIn($name)))
3324
			? $this->joinPathCE($dst, $name) 
3325
			: $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']));
3326
	}
3327
3328
	/**
3329
	 * Move file
3330
	 * Return new file path or false.
3331
	 *
3332
	 * @param  string  $src   source path
3333
	 * @param  string  $dst   destination dir path
3334
	 * @param  string  $name  new file name 
3335
	 * @return string|false
3336
	 * @author Dmitry (dio) Levashov
3337
	 **/
3338
	protected function move($src, $dst, $name) {
3339
		$stat = $this->stat($src);
3340
		$stat['realpath'] = $src;
3341
		$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...
3342
		$this->clearcache();
3343
		
3344 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...
3345
			$this->removed[] = $stat;
3346
3347
			return $this->joinPathCE($dst, $name);
3348
		}
3349
3350
		return $this->setError(elFinder::ERROR_MOVE, $this->path($stat['hash']));
3351
	}
3352
3353
	/**
3354
	 * Copy file from another volume.
3355
	 * Return new file path or false.
3356
	 *
3357
	 * @param  Object  $volume       source volume
3358
	 * @param  string  $src          source file hash
3359
	 * @param  string  $destination  destination dir path
3360
	 * @param  string  $name         file name
3361
	 * @return string|false
3362
	 * @author Dmitry (dio) Levashov
3363
	 **/
3364
	protected function copyFrom($volume, $src, $destination, $name) {
3365
		
3366
		if (($source = $volume->file($src)) == false) {
3367
			return $this->setError(elFinder::ERROR_COPY, '#'.$src, $volume->error());
3368
		}
3369
		
3370
		$errpath = $volume->path($source['hash']);
3371
		
3372
		if (!$this->nameAccepted($source['name'])) {
3373
			return $this->setError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_INVALID_NAME);
3374
		}
3375
				
3376
		if (!$source['read']) {
3377
			return $this->setError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_PERM_DENIED);
3378
		}
3379
		
3380
		if ($source['mime'] == 'directory') {
3381
			$stat = $this->stat($this->joinPathCE($destination, $name));
3382
			$this->clearcache();
3383 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...
3384
				return $this->setError(elFinder::ERROR_COPY, $errpath);
3385
			}
3386
			
3387
			$path = $this->joinPathCE($destination, $name);
3388
			
3389
			foreach ($volume->scandir($src) as $entr) {
3390
				if (!$this->copyFrom($volume, $entr['hash'], $path, $entr['name'])) {
3391
					$this->remove($path, true); // fall back
3392
					return $this->setError($this->error, elFinder::ERROR_COPY, $errpath);
3393
				}
3394
			}
3395
			
3396
		} else {
3397
			// $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...
3398
			// $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...
3399
			if (($dim = $volume->dimensions($src))) {
3400
				$s = explode('x', $dim);
3401
				$source['width']  = $s[0];
3402
				$source['height'] = $s[1];
3403
			}
3404
			
3405
			if (($fp = $volume->open($src)) == false
3406
			|| ($path = $this->saveCE($fp, $destination, $name, $source)) == false) {
3407
				$fp && $volume->close($fp, $src);
3408
				return $this->setError(elFinder::ERROR_COPY, $errpath);
3409
			}
3410
			$volume->close($fp, $src);
3411
			
3412
			// MIME check
3413
			$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 3406 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...
3414
			$mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($stat['name']);
3415
			if ($stat['mime'] === $mimeByName) {
3416
				$mimeByName = '';
3417
			}
3418
			if (!$this->allowPutMime($stat['mime']) || ($mimeByName && $mimeByName !== 'unknown' && !$this->allowPutMime($mimeByName))) {
3419
				$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 3406 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...
3420
				return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME, $errpath);
3421
			}
3422
		}
3423
		
3424
		return $path;
3425
	}
3426
		
3427
	/**
3428
	 * Remove file/ recursive remove dir
3429
	 *
3430
	 * @param  string  $path   file path
3431
	 * @param  bool    $force  try to remove even if file locked
3432
	 * @return bool
3433
	 * @author Dmitry (dio) Levashov
3434
	 **/
3435
	protected function remove($path, $force = false) {
3436
		$stat = $this->stat($path);
3437
		
3438
		if (empty($stat)) {
3439
			return $this->setError(elFinder::ERROR_RM, $this->path($stat['hash']), elFinder::ERROR_FILE_NOT_FOUND);
3440
		}
3441
		
3442
		$stat['realpath'] = $path;
3443
		$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...
3444
		$this->clearcache();
3445
		
3446 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...
3447
			return $this->setError(elFinder::ERROR_LOCKED, $this->path($stat['hash']));
3448
		}
3449
		
3450
		if ($stat['mime'] == 'directory' && empty($stat['thash'])) {
3451
			$ret = $this->delTree($this->convEncIn($path));
3452
			$this->convEncOut();
3453
			if (!$ret) {
3454
				return $this->setError(elFinder::ERROR_RM, $this->path($stat['hash']));
3455
			}
3456 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...
3457
			if ($this->convEncOut(!$this->_unlink($this->convEncIn($path)))) {
3458
				return $this->setError(elFinder::ERROR_RM, $this->path($stat['hash']));
3459
			}
3460
		}
3461
3462
		$this->removed[] = $stat;
3463
		return true;
3464
	}
3465
	
3466
3467
	/************************* thumbnails **************************/
3468
		
3469
	/**
3470
	 * Return thumbnail file name for required file
3471
	 *
3472
	 * @param  array  $stat  file stat
3473
	 * @return string
3474
	 * @author Dmitry (dio) Levashov
3475
	 **/
3476
	protected function tmbname($stat) {
3477
		return $stat['hash'].$stat['ts'].'.png';
3478
	}
3479
	
3480
	/**
3481
	 * Return thumnbnail name if exists
3482
	 *
3483
	 * @param  string  $path file path
3484
	 * @param  array   $stat file stat
3485
	 * @return string|false
3486
	 * @author Dmitry (dio) Levashov
3487
	 **/
3488
	protected function gettmb($path, $stat) {
3489
		if ($this->tmbURL && $this->tmbPath) {
3490
			// file itself thumnbnail
3491
			if (strpos($path, $this->tmbPath) === 0) {
3492
				return basename($path);
3493
			}
3494
3495
			$name = $this->tmbname($stat);
3496
			if (file_exists($this->tmbPath.DIRECTORY_SEPARATOR.$name)) {
3497
				return $name;
3498
			}
3499
		}
3500
		return false;
3501
	}
3502
	
3503
	/**
3504
	 * Return true if thumnbnail for required file can be created
3505
	 *
3506
	 * @param  string  $path  thumnbnail path 
3507
	 * @param  array   $stat  file stat
3508
	 * @param  bool    $checkTmbPath
3509
	 * @return string|bool
3510
	 * @author Dmitry (dio) Levashov
3511
	 **/
3512
	protected function canCreateTmb($path, $stat, $checkTmbPath = true) {
3513
		return (!$checkTmbPath || $this->tmbPathWritable) 
3514
			&& (!$this->tmbPath || strpos($path, $this->tmbPath) === false) // do not create thumnbnail for thumnbnail
3515
			&& $this->imgLib 
3516
			&& strpos($stat['mime'], 'image') === 0 
3517
			&& ($this->imgLib == 'gd' ? $stat['mime'] == 'image/jpeg' || $stat['mime'] == 'image/png' || $stat['mime'] == 'image/gif' : true);
3518
	}
3519
	
3520
	/**
3521
	 * Return true if required file can be resized.
3522
	 * By default - the same as canCreateTmb
3523
	 *
3524
	 * @param  string  $path  thumnbnail path 
3525
	 * @param  array   $stat  file stat
3526
	 * @return string|bool
3527
	 * @author Dmitry (dio) Levashov
3528
	 **/
3529
	protected function canResize($path, $stat) {
3530
		return $this->canCreateTmb($path, $stat, false);
3531
	}
3532
	
3533
	/**
3534
	 * Create thumnbnail and return it's URL on success
3535
	 *
3536
	 * @param  string  $path  file path
3537
	 * @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...
3538
	 * @return string|false
3539
	 * @author Dmitry (dio) Levashov
3540
	 **/
3541
	protected function createTmb($path, $stat) {
3542
		if (!$stat || !$this->canCreateTmb($path, $stat)) {
3543
			return false;
3544
		}
3545
3546
		$name = $this->tmbname($stat);
3547
		$tmb  = $this->tmbPath.DIRECTORY_SEPARATOR.$name;
3548
3549
		// copy image into tmbPath so some drivers does not store files on local fs
3550
		if (($src = $this->fopenCE($path, 'rb')) == false) {
3551
			return false;
3552
		}
3553
3554
		if (($trg = fopen($tmb, 'wb')) == false) {
3555
			$this->fcloseCE($src, $path);
3556
			return false;
3557
		}
3558
3559
		while (!feof($src)) {
3560
			fwrite($trg, fread($src, 8192));
3561
		}
3562
3563
		$this->fcloseCE($src, $path);
3564
		fclose($trg);
3565
3566
		$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...
3567
		
3568
		$tmbSize = $this->tmbSize;
3569
		
3570
		if (($s = getimagesize($tmb)) == false) {
3571
			return false;
3572
		}
3573
3574
		/* If image smaller or equal thumbnail size - just fitting to thumbnail square */
3575
		if ($s[0] <= $tmbSize && $s[1]	<= $tmbSize) {
3576
			$result = $this->imgSquareFit($tmb, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png' );
3577
		} else {
3578
		
3579
			if ($this->options['tmbCrop']) {
3580
		
3581
				$result = $tmb;
3582
				/* Resize and crop if image bigger than thumbnail */
3583 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...
3584
					$result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, false, 'png');
3585
				}
3586
		
3587 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...
3588
					$x = $s[0] > $tmbSize ? intval(($s[0] - $tmbSize)/2) : 0;
3589
					$y = $s[1] > $tmbSize ? intval(($s[1] - $tmbSize)/2) : 0;
3590
					$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...
3591
				} else {
3592
					$result = false;
3593
				}
3594
		
3595
			} else {
3596
				$result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, true, 'png');
3597
			}
3598
		
3599
			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...
3600
				$result = $this->imgSquareFit($result, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png' );
3601
			}
3602
		}
3603
		
3604
		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...
3605
			unlink($tmb);
3606
			return false;
3607
		}
3608
		
3609
		return $name;
3610
	}
3611
3612
	/**
3613
	 * Resize image
3614
	 *
3615
	 * @param  string   $path               image file
3616
	 * @param  int      $width              new width
3617
	 * @param  int      $height             new height
3618
	 * @param  bool	    $keepProportions    crop image
3619
	 * @param  bool	    $resizeByBiggerSide resize image based on bigger side if true
3620
	 * @param  string   $destformat         image destination format
3621
	 * @param  int      $jpgQuality         JEPG quality (1-100)
3622
	 * @return string|false
3623
	 * @author Dmitry (dio) Levashov
3624
	 * @author Alexey Sukhotin
3625
	 **/
3626
	protected function imgResize($path, $width, $height, $keepProportions = false, $resizeByBiggerSide = true, $destformat = null, $jpgQuality = null) {
3627
		if (($s = @getimagesize($path)) == false) {
3628
			return false;
3629
		}
3630
3631
		$result = false;
0 ignored issues
show
Unused Code introduced by
$result is not used, you could remove the assignment.

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

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

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

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

Loading history...
3632
		
3633
		list($size_w, $size_h) = array($width, $height);
3634
	
3635
		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...
3636
			$jpgQuality = $this->options['jpgQuality'];
3637
		}
3638
		
3639
		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...
3640
		
3641
			list($orig_w, $orig_h) = array($s[0], $s[1]);
3642
		
3643
			/* Resizing by biggest side */
3644
			if ($resizeByBiggerSide) {
3645 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...
3646
					$size_h = round($orig_h * $width / $orig_w);
3647
					$size_w = $width;
3648
				} else {
3649
					$size_w = round($orig_w * $height / $orig_h);
3650
					$size_h = $height;
3651
				}
3652 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...
3653
				if ($orig_w > $orig_h) {
3654
					$size_w = round($orig_w * $height / $orig_h);
3655
					$size_h = $height;
3656
				} else {
3657
					$size_h = round($orig_h * $width / $orig_w);
3658
					$size_w = $width;
3659
				}
3660
			}
3661
		}
3662
3663
		switch ($this->imgLib) {
3664
			case 'imagick':
3665
				
3666
				try {
3667
					$img = new imagick($path);
3668
				} catch (Exception $e) {
3669
					return false;
3670
				}
3671
3672
				// Imagick::FILTER_BOX faster than FILTER_LANCZOS so use for createTmb
3673
				// 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...
3674
				// 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...
3675
				$filter = ($destformat === 'png' /* createTmb */)? Imagick::FILTER_BOX : Imagick::FILTER_LANCZOS;
3676
				
3677
				$ani = ($img->getNumberImages() > 1);
3678
				if ($ani && is_null($destformat)) {
3679
					$img = $img->coalesceImages();
3680
					do {
3681
						$img->resizeImage($size_w, $size_h, $filter, 1);
3682
					} while ($img->nextImage());
3683
					$img = $img->optimizeImageLayers();
3684
					$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...
3685
				} else {
3686
					if ($ani) {
3687
						$img->setFirstIterator();
3688
					}
3689
					$img->resizeImage($size_w, $size_h, $filter, 1);
3690
					$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...
3691
				}
3692
				
3693
				$img->destroy();
3694
3695
				return $result ? $path : false;
3696
3697
				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...
3698
3699
			case 'gd':
3700
				$img = $this->gdImageCreate($path,$s['mime']);
3701
3702
				if ($img &&  false != ($tmp = imagecreatetruecolor($size_w, $size_h))) {
3703
				
3704
					$this->gdImageBackground($tmp,$this->options['tmbBgColor']);
3705
					
3706
					if (!imagecopyresampled($tmp, $img, 0, 0, 0, 0, $size_w, $size_h, $s[0], $s[1])) {
3707
						return false;
3708
					}
3709
		
3710
					$result = $this->gdImage($tmp, $path, $destformat, $s['mime'], $jpgQuality);
3711
3712
					imagedestroy($img);
3713
					imagedestroy($tmp);
3714
3715
					return $result ? $path : false;
3716
3717
				}
3718
				break;
3719
		}
3720
		
3721
		return false;
3722
  	}
3723
  
3724
	/**
3725
	 * Crop image
3726
	 *
3727
	 * @param  string   $path               image file
3728
	 * @param  int      $width              crop width
3729
	 * @param  int      $height             crop height
3730
	 * @param  bool	    $x                  crop left offset
3731
	 * @param  bool	    $y                  crop top offset
3732
	 * @param  string   $destformat         image destination format
3733
	 * @param  int      $jpgQuality         JEPG quality (1-100)
3734
	 * @return string|false
3735
	 * @author Dmitry (dio) Levashov
3736
	 * @author Alexey Sukhotin
3737
	 **/
3738
  	protected function imgCrop($path, $width, $height, $x, $y, $destformat = null, $jpgQuality = null) {
3739
		if (($s = @getimagesize($path)) == false) {
3740
			return false;
3741
		}
3742
3743
		$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...
3744
		
3745
		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...
3746
			$jpgQuality = $this->options['jpgQuality'];
3747
		}
3748
3749
		switch ($this->imgLib) {
3750
			case 'imagick':
3751
				
3752
				try {
3753
					$img = new imagick($path);
3754
				} catch (Exception $e) {
3755
					return false;
3756
				}
3757
				
3758
				$ani = ($img->getNumberImages() > 1);
3759
				if ($ani && is_null($destformat)) {
3760
					$img = $img->coalesceImages();
3761
					do {
3762
						$img->setImagePage($s[0], $s[1], 0, 0);
3763
						$img->cropImage($width, $height, $x, $y);
3764
						$img->setImagePage($width, $height, 0, 0);
3765
					} while ($img->nextImage());
3766
					$img = $img->optimizeImageLayers();
3767
					$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...
3768
				} else {
3769
					if ($ani) {
3770
						$img->setFirstIterator();
3771
					}
3772
					$img->setImagePage($s[0], $s[1], 0, 0);
3773
					$img->cropImage($width, $height, $x, $y);
3774
					$img->setImagePage($width, $height, 0, 0);
3775
					$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...
3776
				}
3777
				
3778
				$img->destroy();
3779
3780
				return $result ? $path : false;
3781
3782
				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...
3783
3784
			case 'gd':
3785
				$img = $this->gdImageCreate($path,$s['mime']);
3786
3787
				if ($img &&  false != ($tmp = imagecreatetruecolor($width, $height))) {
3788
					
3789
					$this->gdImageBackground($tmp,$this->options['tmbBgColor']);
3790
3791
					$size_w = $width;
3792
					$size_h = $height;
3793
3794
					if ($s[0] < $width || $s[1] < $height) {
3795
						$size_w = $s[0];
3796
						$size_h = $s[1];
3797
					}
3798
3799
					if (!imagecopy($tmp, $img, 0, 0, $x, $y, $size_w, $size_h)) {
3800
						return false;
3801
					}
3802
					
3803
					$result = $this->gdImage($tmp, $path, $destformat, $s['mime'], $jpgQuality);
3804
3805
					imagedestroy($img);
3806
					imagedestroy($tmp);
3807
3808
					return $result ? $path : false;
3809
3810
				}
3811
				break;
3812
		}
3813
3814
		return false;
3815
	}
3816
3817
	/**
3818
	 * Put image to square
3819
	 *
3820
	 * @param  string   $path               image file
3821
	 * @param  int      $width              square width
3822
	 * @param  int      $height             square height
3823
	 * @param  int	    $align              reserved
3824
	 * @param  int 	    $valign             reserved
3825
	 * @param  string   $bgcolor            square background color in #rrggbb format
3826
	 * @param  string   $destformat         image destination format
3827
	 * @param  int      $jpgQuality         JEPG quality (1-100)
3828
	 * @return string|false
3829
	 * @author Dmitry (dio) Levashov
3830
	 * @author Alexey Sukhotin
3831
	 **/
3832
	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...
3833
		if (($s = @getimagesize($path)) == false) {
3834
			return false;
3835
		}
3836
3837
		$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...
3838
3839
		/* Coordinates for image over square aligning */
3840
		$y = ceil(abs($height - $s[1]) / 2); 
3841
		$x = ceil(abs($width - $s[0]) / 2);
3842
3843
		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...
3844
			$jpgQuality = $this->options['jpgQuality'];
3845
		}
3846
3847
		switch ($this->imgLib) {
3848
			case 'imagick':
3849
				try {
3850
					$img = new imagick($path);
3851
				} catch (Exception $e) {
3852
					return false;
3853
				}
3854
				
3855
				$ani = ($img->getNumberImages() > 1);
3856
				if ($ani && is_null($destformat)) {
3857
					$img1 = new Imagick();
3858
					$img1->setFormat('gif');
3859
					$img = $img->coalesceImages();
3860
					do {
3861
						$gif = new Imagick();
3862
						$gif->newImage($width, $height, new ImagickPixel($bgcolor));
3863
						$gif->setImageColorspace($img->getImageColorspace());
3864
						$gif->setImageFormat('gif');
3865
						$gif->compositeImage( $img, imagick::COMPOSITE_OVER, $x, $y );
3866
						$gif->setImageDelay($img->getImageDelay());
3867
						$gif->setImageIterations($img->getImageIterations());
3868
						$img1->addImage($gif);
3869
						$gif->destroy();
3870
					} while ($img->nextImage());
3871
					$img1 = $img1->optimizeImageLayers();
3872
					$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...
3873
				} else {
3874
					if ($ani) {
3875
						$img->setFirstIterator();
3876
					}
3877
					$img1 = new Imagick();
3878
					$img1->newImage($width, $height, new ImagickPixel($bgcolor));
3879
					$img1->setImageColorspace($img->getImageColorspace());
3880
					$img1->compositeImage( $img, imagick::COMPOSITE_OVER, $x, $y );
3881
					$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...
3882
				}
3883
				
3884
				$img1->destroy();
3885
				$img->destroy();
3886
				return $result ? $path : false;
3887
3888
				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...
3889
3890
			case 'gd':
3891
				$img = $this->gdImageCreate($path,$s['mime']);
3892
3893
				if ($img &&  false != ($tmp = imagecreatetruecolor($width, $height))) {
3894
3895
					$this->gdImageBackground($tmp,$bgcolor);
3896
3897
					if (!imagecopy($tmp, $img, $x, $y, 0, 0, $s[0], $s[1])) {
3898
						return false;
3899
					}
3900
3901
					$result = $this->gdImage($tmp, $path, $destformat, $s['mime'], $jpgQuality);
3902
3903
					imagedestroy($img);
3904
					imagedestroy($tmp);
3905
3906
					return $result ? $path : false;
3907
				}
3908
				break;
3909
		}
3910
3911
		return false;
3912
	}
3913
3914
	/**
3915
	 * Rotate image
3916
	 *
3917
	 * @param  string   $path               image file
3918
	 * @param  int      $degree             rotete degrees
3919
	 * @param  string   $bgcolor            square background color in #rrggbb format
3920
	 * @param  string   $destformat         image destination format
3921
	 * @param  int      $jpgQuality         JEPG quality (1-100)
3922
	 * @return string|false
3923
	 * @author nao-pon
3924
	 * @author Troex Nevelin
3925
	 **/
3926
	protected function imgRotate($path, $degree, $bgcolor = '#ffffff', $destformat = null, $jpgQuality = null) {
3927
		if (($s = @getimagesize($path)) == false || $degree % 360 === 0) {
3928
			return false;
3929
		}
3930
3931
		$result = false;
3932
3933
		// try lossless rotate
3934
		if ($degree % 90 === 0 && in_array($s[2], array(IMAGETYPE_JPEG, IMAGETYPE_JPEG2000))) {
3935
			$count = ($degree / 90) % 4;
3936
			$exiftran = array(
3937
				1 => '-9',
3938
				2 => '-1',
3939
				3 => '-2'
3940
			);
3941
			$jpegtran = array(
3942
				1 => '90',
3943
				2 => '180',
3944
				3 => '270'
3945
			);
3946
			$quotedPath = escapeshellarg($path);
3947
			$cmds = array(
3948
				'exiftran -i '.$exiftran[$count].' '.$path,
3949
				'jpegtran -rotate '.$jpegtran[$count].' -copy all -outfile '.$quotedPath.' '.$quotedPath
3950
			);
3951
			foreach($cmds as $cmd) {
3952
				if ($this->procExec($cmd) === 0) {
3953
					$result = true;
3954
					break;
3955
				}
3956
			}
3957
			if ($result) {
3958
				return $path;
3959
			}
3960
		}
3961
3962
		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...
3963
			$jpgQuality = $this->options['jpgQuality'];
3964
		}
3965
3966
		switch ($this->imgLib) {
3967
			case 'imagick':
3968
				try {
3969
					$img = new imagick($path);
3970
				} catch (Exception $e) {
3971
					return false;
3972
				}
3973
3974
				if ($img->getNumberImages() > 1) {
3975
					$img = $img->coalesceImages();
3976
					do {
3977
						$img->rotateImage(new ImagickPixel($bgcolor), $degree);
3978
					} while ($img->nextImage());
3979
					$img = $img->optimizeImageLayers();
3980
					$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...
3981
				} else {
3982
					$img->rotateImage(new ImagickPixel($bgcolor), $degree);
3983
					$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...
3984
				}
3985
				$img->destroy();
3986
				return $result ? $path : false;
3987
3988
				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...
3989
3990
			case 'gd':
3991
				$img = $this->gdImageCreate($path,$s['mime']);
3992
3993
				$degree = 360 - $degree;
3994
				list($r, $g, $b) = sscanf($bgcolor, "#%02x%02x%02x");
3995
				$bgcolor = imagecolorallocate($img, $r, $g, $b);
3996
				$tmp = imageRotate($img, $degree, (int)$bgcolor);
3997
3998
				$result = $this->gdImage($tmp, $path, $destformat, $s['mime'], $jpgQuality);
3999
4000
				imageDestroy($img);
4001
				imageDestroy($tmp);
4002
4003
				return $result ? $path : false;
4004
4005
				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...
4006
		}
4007
4008
		return false;
4009
	}
4010
4011
	/**
4012
	 * Execute shell command
4013
	 *
4014
	 * @param  string  $command       command line
4015
	 * @param  array   $output        stdout strings
4016
	 * @param  array   $return_var    process exit code
4017
	 * @param  array   $error_output  stderr strings
4018
	 * @return int     exit code
4019
	 * @author Alexey Sukhotin
4020
	 **/
4021
	protected function procExec($command , array &$output = null, &$return_var = -1, array &$error_output = null) {
4022
4023
		$descriptorspec = array(
4024
			0 => array("pipe", "r"),  // stdin
4025
			1 => array("pipe", "w"),  // stdout
4026
			2 => array("pipe", "w")   // stderr
4027
		);
4028
4029
		$process = proc_open($command, $descriptorspec, $pipes, null, null);
4030
4031
		if (is_resource($process)) {
4032
4033
			fclose($pipes[0]);
4034
4035
			$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...
4036
			$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...
4037
4038
			$output = stream_get_contents($pipes[1]);
4039
			$error_output = stream_get_contents($pipes[2]);
4040
4041
			fclose($pipes[1]);
4042
			fclose($pipes[2]);
4043
			$return_var = proc_close($process);
4044
4045
4046
		}
4047
		
4048
		return $return_var;
4049
		
4050
	}
4051
4052
	/**
4053
	 * Remove thumbnail, also remove recursively if stat is directory
4054
	 *
4055
	 * @param  string  $stat  file stat
4056
	 * @return void
4057
	 * @author Dmitry (dio) Levashov
4058
	 * @author Naoki Sawada
4059
	 * @author Troex Nevelin
4060
	 **/
4061
	protected function rmTmb($stat) {
4062
		if ($stat['mime'] === 'directory') {
4063
			foreach ($this->scandirCE($this->decode($stat['hash'])) as $p) {
4064
				@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...
4065
				$name = $this->basenameCE($p);
4066
				$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...
4067
			}
4068
		} else if (!empty($stat['tmb']) && $stat['tmb'] != "1") {
4069
			$tmb = $this->tmbPath.DIRECTORY_SEPARATOR.$stat['tmb'];
4070
			file_exists($tmb) && @unlink($tmb);
4071
			clearstatcache();
4072
		}
4073
	}
4074
	
4075
	/**
4076
	 * Create an gd image according to the specified mime type
4077
	 *
4078
	 * @param string $path image file
4079
	 * @param string $mime
4080
	 * @return gd image resource identifier
4081
	 */
4082
	protected function gdImageCreate($path,$mime){
4083
		switch($mime){
4084
			case 'image/jpeg':
4085
			return imagecreatefromjpeg($path);
4086
4087
			case 'image/png':
4088
			return imagecreatefrompng($path);
4089
4090
			case 'image/gif':
4091
			return imagecreatefromgif($path);
4092
4093
			case 'image/xbm':
4094
			return imagecreatefromxbm($path);
4095
		}
4096
		return false;
4097
	}
4098
4099
	/**
4100
	 * Output gd image to file
4101
	 *
4102
	 * @param resource $image gd image resource
4103
	 * @param string $filename The path to save the file to.
4104
	 * @param string $destformat The Image type to use for $filename
4105
	 * @param string $mime The original image mime type
4106
	 * @param int    $jpgQuality  JEPG quality (1-100)
4107
	 */
4108
	protected function gdImage($image, $filename, $destformat, $mime, $jpgQuality = null ){
4109
4110
		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...
4111
			$jpgQuality = $this->options['jpgQuality'];
4112
		}
4113 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...
4114
			return imagejpeg($image, $filename, $jpgQuality);
4115
		}
4116
4117 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...
4118
			return imagegif($image, $filename, 7);
4119
		}
4120
4121
		return imagepng($image, $filename, 7);
4122
	}
4123
4124
	/**
4125
	 * Output imagick image to file
4126
	 *
4127
	 * @param resource $img imagick image resource
4128
	 * @param string $filename The path to save the file to.
4129
	 * @param string $destformat The Image type to use for $filename
4130
	 * @param int    $jpgQuality  JEPG quality (1-100)
4131
	 */
4132
	protected function imagickImage($img, $filename, $destformat, $jpgQuality = null ){
4133
4134
		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...
4135
			$jpgQuality = $this->options['jpgQuality'];
4136
		}
4137
		
4138
		try {
4139
			if ($destformat) {
4140
				if ($destformat === 'gif') {
4141
					$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...
4142
				} else if ($destformat === 'png') {
4143
					$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...
4144
				} else if ($destformat === 'jpg') {
4145
					$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...
4146
				}
4147
			}
4148 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...
4149
				$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...
4150
				$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...
4151
				try {
4152
					$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...
4153
				} catch (ImagickException $e) {
4154
					$orientation = 0;
4155
				}
4156
				$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...
4157
				if ($orientation) {
4158
					$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...
4159
				}
4160
			}
4161
			$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...
4162
		} catch (Exception $e) {
4163
			$result = false;
4164
		}
4165
		
4166
		return $result;
4167
		
4168
		
4169
		
4170 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...
4171
			return imagejpeg($image, $filename, $jpgQuality);
4172
		}
4173
4174 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...
4175
			return imagegif($image, $filename, 7);
4176
		}
4177
4178
		return imagepng($image, $filename, 7);
4179
	}
4180
4181
	/**
4182
	 * Assign the proper background to a gd image
4183
	 *
4184
	 * @param resource $image gd image resource
4185
	 * @param string $bgcolor background color in #rrggbb format
4186
	 */
4187
	protected function gdImageBackground($image, $bgcolor){
4188
4189
		if( $bgcolor == 'transparent' ){
4190
			imagesavealpha($image,true);
4191
			$bgcolor1 = imagecolorallocatealpha($image, 255, 255, 255, 127);
4192
4193
		}else{
4194
			list($r, $g, $b) = sscanf($bgcolor, "#%02x%02x%02x");
4195
			$bgcolor1 = imagecolorallocate($image, $r, $g, $b);
4196
		}
4197
4198
		imagefill($image, 0, 0, $bgcolor1);
4199
	}
4200
4201
	/*********************** misc *************************/
4202
	
4203
	/**
4204
	 * Return smart formatted date
4205
	 *
4206
	 * @param  int     $ts  file timestamp
4207
	 * @return string
4208
	 * @author Dmitry (dio) Levashov
4209
	 **/
4210
	// 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...
4211
	// 	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...
4212
	// 		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...
4213
	// 	}
4214
	// 	
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...
4215
	// 	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...
4216
	// 		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...
4217
	// 	} 
4218
	// 	
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...
4219
	// 	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...
4220
	// }
4221
4222
	/**
4223
	* Find position of first occurrence of string in a string with multibyte support
4224
	*
4225
	* @param  string  $haystack  The string being checked.
4226
	* @param  string  $needle    The string to find in haystack.
4227
	* @param  int     $offset    The search offset. If it is not specified, 0 is used.
4228
	* @return int|bool
4229
	* @author Alexey Sukhotin
4230
	**/
4231
	protected function stripos($haystack , $needle , $offset = 0) {
4232
		if (function_exists('mb_stripos')) {
4233
			return mb_stripos($haystack , $needle , $offset, 'UTF-8');
4234
		} else if (function_exists('mb_strtolower') && function_exists('mb_strpos')) {
4235
			return mb_strpos(mb_strtolower($haystack, 'UTF-8'), mb_strtolower($needle, 'UTF-8'), $offset);
4236
		} 
4237
		return stripos($haystack , $needle , $offset);
4238
	}
4239
4240
	/**
4241
	 * Get server side available archivers
4242
	 * 
4243
	 * @param bool $use_cache
4244
	 * @return array
4245
	 */
4246
	protected function getArchivers($use_cache = true) {
4247
4248
		$sessionKey = 'ARCHIVERS_CACHE';
4249
		if ($use_cache && isset($this->sessionCache[$sessionKey]) && is_array($this->sessionCache[$sessionKey])) {
4250
			return $this->sessionCache[$sessionKey];
4251
		}
4252
		
4253
		$arcs = array(
4254
			'create'  => array(),
4255
			'extract' => array()
4256
		);
4257
		
4258
		if (function_exists('proc_open')) {
4259
		
4260
			$this->procExec('tar --version', $o, $ctar);
4261
			
4262
			if ($ctar == 0) {
4263
				$arcs['create']['application/x-tar']  = array('cmd' => 'tar', 'argc' => '-cf', 'ext' => 'tar');
4264
				$arcs['extract']['application/x-tar'] = array('cmd' => 'tar', 'argc' => '-xf', 'ext' => 'tar');
4265
				unset($o);
4266
				$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...
4267 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...
4268
					$arcs['create']['application/x-gzip']  = array('cmd' => 'tar', 'argc' => '-czf', 'ext' => 'tgz');
4269
					$arcs['extract']['application/x-gzip'] = array('cmd' => 'tar', 'argc' => '-xzf', 'ext' => 'tgz');
4270
				}
4271
				unset($o);
4272
				$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...
4273 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...
4274
					$arcs['create']['application/x-bzip2']  = array('cmd' => 'tar', 'argc' => '-cjf', 'ext' => 'tbz');
4275
					$arcs['extract']['application/x-bzip2'] = array('cmd' => 'tar', 'argc' => '-xjf', 'ext' => 'tbz');
4276
				}
4277
				unset($o);
4278
				$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...
4279 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...
4280
					$arcs['create']['application/x-xz']  = array('cmd' => 'tar', 'argc' => '-cJf', 'ext' => 'xz');
4281
					$arcs['extract']['application/x-xz'] = array('cmd' => 'tar', 'argc' => '-xJf', 'ext' => 'xz');
4282
				}
4283
			}
4284
			unset($o);
4285
			$this->procExec('zip -v', $o, $c);
4286
			if ($c == 0) {
4287
				$arcs['create']['application/zip']  = array('cmd' => 'zip', 'argc' => '-r9', 'ext' => 'zip');
4288
			}
4289
			unset($o);
4290
			$this->procExec('unzip --help', $o, $c);
4291
			if ($c == 0) {
4292
				$arcs['extract']['application/zip'] = array('cmd' => 'unzip', 'argc' => '',  'ext' => 'zip');
4293
			}
4294
			unset($o);
4295
			$this->procExec('rar --version', $o, $c);
4296
			if ($c == 0 || $c == 7) {
4297
				$arcs['create']['application/x-rar']  = array('cmd' => 'rar', 'argc' => 'a -inul', 'ext' => 'rar');
4298
				$arcs['extract']['application/x-rar'] = array('cmd' => 'rar', 'argc' => 'x -y',    'ext' => 'rar');
4299
			} else {
4300
				unset($o);
4301
				$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...
4302
				if ($c==0 || $c == 7) {
4303
					$arcs['extract']['application/x-rar'] = array('cmd' => 'unrar', 'argc' => 'x -y', 'ext' => 'rar');
4304
				}
4305
			}
4306
			unset($o);
4307
			$this->procExec('7za --help', $o, $c);
4308
			if ($c == 0) {
4309
				$arcs['create']['application/x-7z-compressed']  = array('cmd' => '7za', 'argc' => 'a', 'ext' => '7z');
4310
				$arcs['extract']['application/x-7z-compressed'] = array('cmd' => '7za', 'argc' => 'x -y', 'ext' => '7z');
4311
				
4312
				if (empty($arcs['create']['application/zip'])) {
4313
					$arcs['create']['application/zip'] = array('cmd' => '7za', 'argc' => 'a -tzip', 'ext' => 'zip');
4314
				}
4315 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...
4316
					$arcs['extract']['application/zip'] = array('cmd' => '7za', 'argc' => 'x -tzip -y', 'ext' => 'zip');
4317
				}
4318
				if (empty($arcs['create']['application/x-tar'])) {
4319
					$arcs['create']['application/x-tar'] = array('cmd' => '7za', 'argc' => 'a -ttar', 'ext' => 'tar');
4320
				}
4321 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...
4322
					$arcs['extract']['application/x-tar'] = array('cmd' => '7za', 'argc' => 'x -ttar -y', 'ext' => 'tar');
4323
				}
4324
			} else if (substr(PHP_OS,0,3) === 'WIN') {
4325
				// check `7z` for Windows server.
4326
				unset($o);
4327
				$this->procExec('7z', $o, $c);
4328
				if ($c == 0) {
4329
					$arcs['create']['application/x-7z-compressed']  = array('cmd' => '7z', 'argc' => 'a', 'ext' => '7z');
4330
					$arcs['extract']['application/x-7z-compressed'] = array('cmd' => '7z', 'argc' => 'x -y', 'ext' => '7z');
4331
					
4332
					if (empty($arcs['create']['application/zip'])) {
4333
						$arcs['create']['application/zip'] = array('cmd' => '7z', 'argc' => 'a -tzip', 'ext' => 'zip');
4334
					}
4335
					if (empty($arcs['extract']['application/zip'])) {
4336
						$arcs['extract']['application/zip'] = array('cmd' => '7z', 'argc' => 'x -tzip -y', 'ext' => 'zip');
4337
					}
4338
					if (empty($arcs['create']['application/x-tar'])) {
4339
						$arcs['create']['application/x-tar'] = array('cmd' => '7z', 'argc' => 'a -ttar', 'ext' => 'tar');
4340
					}
4341
					if (empty($arcs['extract']['application/x-tar'])) {
4342
						$arcs['extract']['application/x-tar'] = array('cmd' => '7z', 'argc' => 'x -ttar -y', 'ext' => 'tar');
4343
					}
4344
				}
4345
			}
4346
		
4347
		}
4348
		
4349
		// Use PHP ZipArchive Class
4350
		if (class_exists('ZipArchive', false)) {
4351
			if (empty($arcs['create']['application/zip'])) {
4352
				$arcs['create']['application/zip']  = array('cmd' => 'phpfunction', 'argc' => 'self::zipArchiveZip', 'ext' => 'zip');
4353
			}
4354 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...
4355
				$arcs['extract']['application/zip'] = array('cmd' => 'phpfunction', 'argc' => 'self::zipArchiveUnzip', 'ext' => 'zip');
4356
			}
4357
		}
4358
		
4359
		$this->sessionCache[$sessionKey] = $arcs;
4360
		return $arcs;
4361
	}
4362
4363
	/**
4364
	 * Resolve relative / (Unix-like)absolute path
4365
	 * 
4366
	 * @param string $path  target path
4367
	 * @param string $base  base path
4368
	 * @return string
4369
	 */
4370
	protected function getFullPath($path, $base) {
4371
		$separator = $this->separator;
4372
		$systemroot = $this->systemRoot;
4373
4374
		$sepquoted = preg_quote($separator, '#');
4375
4376
		// normalize `/../`
4377
		$normreg = '#('.$sepquoted.')[^'.$sepquoted.']+'.$sepquoted.'\.\.'.$sepquoted.'#';
4378
		while(preg_match($normreg, $path)) {
4379
			$path = preg_replace($normreg, '$1', $path);
4380
		}
4381
		
4382
		// 'Here'
4383
		if ($path === '' || $path === '.' . $separator) return $base;
4384
		
4385
		// Absolute path
4386
		if ($path[0] === $separator || strpos($path, $systemroot) === 0) {
4387
			return $path;
4388
		}
4389
		
4390
		$preg_separator = '#' . $sepquoted . '#';
4391
		
4392
		// Relative path from 'Here'
4393
		if (substr($path, 0, 2) === '.' . $separator || $path[0] !== '.' || substr($path, 0, 3) !== '..' . $separator) {
4394
			$arrn = preg_split($preg_separator, $path, -1, PREG_SPLIT_NO_EMPTY);
4395
			if ($arrn[0] !== '.') {
4396
				array_unshift($arrn, '.');
4397
			}
4398
			$arrn[0] = $base;
4399
			return join($separator, $arrn);
4400
		}
4401
		
4402
		// Relative path from dirname()
4403
		if (substr($path, 0, 3) === '../') {
4404
			$arrn = preg_split($preg_separator, $path, -1, PREG_SPLIT_NO_EMPTY);
4405
			$arrp = preg_split($preg_separator, $base, -1, PREG_SPLIT_NO_EMPTY);
4406
		
4407
			while (! empty($arrn) && $arrn[0] === '..') {
4408
				array_shift($arrn);
4409
				array_pop($arrp);
4410
			}
4411
			$path = ! empty($arrp) ? $systemroot . join($separator, array_merge($arrp, $arrn)) :
4412
				(! empty($arrn) ? $systemroot . join($separator, $arrn) : $systemroot);
4413
		}
4414
		
4415
		return $path;
4416
	}
4417
4418
	/**
4419
	 * Remove directory recursive on local file system
4420
	 *
4421
	 * @param string $dir Target dirctory path
4422
	 * @return boolean
4423
	 * @author Naoki Sawada
4424
	 */
4425
	public function rmdirRecursive($dir) {
4426
		if (!is_link($dir) && is_dir($dir)) {
4427
			@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...
4428
			foreach (array_diff(scandir($dir), array('.', '..')) as $file) {
4429
				@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...
4430
				$path = $dir . DIRECTORY_SEPARATOR . $file;
4431
				if (!is_link($dir) && is_dir($path)) {
4432
					$this->rmdirRecursive($path);
4433
				} else {
4434
					@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...
4435
					@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...
4436
				}
4437
			}
4438
			return @rmdir($dir);
4439
		} else if (is_file($dir) || is_link($dir)) {
4440
			@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...
4441
			return @unlink($dir);
4442
		}
4443
		return false;
4444
	}
4445
4446
	/**
4447
	 * Create archive and return its path
4448
	 *
4449
	 * @param  string  $dir    target dir
4450
	 * @param  array   $files  files names list
4451
	 * @param  string  $name   archive name
4452
	 * @param  array   $arc    archiver options
4453
	 * @return string|bool
4454
	 * @author Dmitry (dio) Levashov, 
4455
	 * @author Alexey Sukhotin
4456
	 * @author Naoki Sawada
4457
	 **/
4458
	protected function makeArchive($dir, $files, $name, $arc) {
4459
		if ($arc['cmd'] === 'phpfunction') {
4460 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...
4461
				call_user_func_array($arc['argc'], array($dir, $files, $name));
4462
			}
4463
		} else {
4464
			$cwd = getcwd();
4465
			chdir($dir);
4466
			
4467
			$files = array_map('escapeshellarg', $files);
4468
			
4469
			$cmd = $arc['cmd'].' '.$arc['argc'].' '.escapeshellarg($name).' '.implode(' ', $files);
4470
			$this->procExec($cmd, $o, $c);
4471
			chdir($cwd);
4472
		}
4473
		$path = $dir.DIRECTORY_SEPARATOR.$name;
4474
		return file_exists($path) ? $path : false;
4475
	}
4476
	
4477
	/**
4478
	 * Unpack archive
4479
	 *
4480
	 * @param  string  $path   archive path
4481
	 * @param  array   $arc    archiver command and arguments (same as in $this->archivers)
4482
	 * @param  bool    $remove remove archive ( unlink($path) )
4483
	 * @return void
4484
	 * @author Dmitry (dio) Levashov
4485
	 * @author Alexey Sukhotin
4486
	 * @author Naoki Sawada
4487
	 **/
4488
	protected function unpackArchive($path, $arc, $remove = true) {
4489
		$dir = dirname($path);
4490
		if ($arc['cmd'] === 'phpfunction') {
4491 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...
4492
				call_user_func_array($arc['argc'], array($path, $dir));
4493
			}
4494
		} else {
4495
			$cwd = getcwd();
4496
			chdir($dir);
4497
			$cmd = $arc['cmd'].' '.$arc['argc'].' '.escapeshellarg(basename($path));
4498
			$this->procExec($cmd, $o, $c);
4499
			chdir($cwd);
4500
		}
4501
		$remove && unlink($path);
4502
	}
4503
	
4504
	/**
4505
	 * Create Zip archive using PHP class ZipArchive
4506
	 * 
4507
	 * @param  string        $dir      target dir
4508
	 * @param  array         $files    files names list
4509
	 * @param  string|object $zipPath  Zip archive name
4510
	 * @return void
4511
	 * @author Naoki Sawada
4512
	 */
4513
	protected static function zipArchiveZip($dir, $files, $zipPath) {
4514
		try {
4515
			if ($start = is_string($zipPath)) {
4516
				$zip = new ZipArchive();
4517
				if ($zip->open($dir . DIRECTORY_SEPARATOR . $zipPath, ZipArchive::CREATE) !== true) {
4518
					$zip = false;
4519
				}
4520
			} else {
4521
				$zip = $zipPath;
4522
			}
4523
			if ($zip) {
4524
				foreach($files as $file) {
4525
					$path = $dir . DIRECTORY_SEPARATOR . $file;
4526
					if (is_dir($path)) {
4527
						$zip->addEmptyDir($file);
4528
						$_files = array();
4529
						if ($handle = opendir($path)) {
4530
							while (false !== ($entry = readdir($handle))) {
4531
								if ($entry !== "." && $entry !== "..") {
4532
									$_files[] = $file . DIRECTORY_SEPARATOR . $entry;
4533
								}
4534
							}
4535
							closedir($handle);
4536
						}
4537
						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...
4538
							self::zipArchiveZip($dir, $_files, $zip);
4539
						}
4540
					} else {
4541
						$zip->addFile($path, $file);
4542
					}
4543
				}
4544
				$start && $zip->close();
4545
			}
4546
		} catch (Exception $e) {
4547
			return false;
4548
		}
4549
		return true;
4550
	}
4551
	
4552
	/**
4553
	 * Unpack Zip archive using PHP class ZipArchive
4554
	 * 
4555
	 * @param  string $zipPath  Zip archive name
4556
	 * @param  string $toDir    Extract to path
4557
	 * @return bool
4558
	 * @author Naoki Sawada
4559
	 */
4560
	protected static function zipArchiveUnzip($zipPath, $toDir) {
4561
		try {
4562
			$zip = new ZipArchive();
4563
			if ($zip->open($zipPath) === true) {
4564
				$zip->extractTo($toDir);
4565
				$zip->close();
4566
			}
4567
		} catch (Exception $e) {
4568
			return false;
4569
		}
4570
		return true;
4571
	}
4572
	
4573
	/**==================================* abstract methods *====================================**/
4574
	
4575
	/*********************** paths/urls *************************/
4576
	
4577
	/**
4578
	 * Return parent directory path
4579
	 *
4580
	 * @param  string  $path  file path
4581
	 * @return string
4582
	 * @author Dmitry (dio) Levashov
4583
	 **/
4584
	abstract protected function _dirname($path);
4585
4586
	/**
4587
	 * Return file name
4588
	 *
4589
	 * @param  string  $path  file path
4590
	 * @return string
4591
	 * @author Dmitry (dio) Levashov
4592
	 **/
4593
	abstract protected function _basename($path);
4594
4595
	/**
4596
	 * Join dir name and file name and return full path.
4597
	 * Some drivers (db) use int as path - so we give to concat path to driver itself
4598
	 *
4599
	 * @param  string  $dir   dir path
4600
	 * @param  string  $name  file name
4601
	 * @return string
4602
	 * @author Dmitry (dio) Levashov
4603
	 **/
4604
	abstract protected function _joinPath($dir, $name);
4605
4606
	/**
4607
	 * Return normalized path 
4608
	 *
4609
	 * @param  string  $path  file path
4610
	 * @return string
4611
	 * @author Dmitry (dio) Levashov
4612
	 **/
4613
	abstract protected function _normpath($path);
4614
4615
	/**
4616
	 * Return file path related to root dir
4617
	 *
4618
	 * @param  string  $path  file path
4619
	 * @return string
4620
	 * @author Dmitry (dio) Levashov
4621
	 **/
4622
	abstract protected function _relpath($path);
4623
	
4624
	/**
4625
	 * Convert path related to root dir into real path
4626
	 *
4627
	 * @param  string  $path  rel file path
4628
	 * @return string
4629
	 * @author Dmitry (dio) Levashov
4630
	 **/
4631
	abstract protected function _abspath($path);
4632
	
4633
	/**
4634
	 * Return fake path started from root dir.
4635
	 * Required to show path on client side.
4636
	 *
4637
	 * @param  string  $path  file path
4638
	 * @return string
4639
	 * @author Dmitry (dio) Levashov
4640
	 **/
4641
	abstract protected function _path($path);
4642
	
4643
	/**
4644
	 * Return true if $path is children of $parent
4645
	 *
4646
	 * @param  string  $path    path to check
4647
	 * @param  string  $parent  parent path
4648
	 * @return bool
4649
	 * @author Dmitry (dio) Levashov
4650
	 **/
4651
	abstract protected function _inpath($path, $parent);
4652
	
4653
	/**
4654
	 * Return stat for given path.
4655
	 * Stat contains following fields:
4656
	 * - (int)    size    file size in b. required
4657
	 * - (int)    ts      file modification time in unix time. required
4658
	 * - (string) mime    mimetype. required for folders, others - optionally
4659
	 * - (bool)   read    read permissions. required
4660
	 * - (bool)   write   write permissions. required
4661
	 * - (bool)   locked  is object locked. optionally
4662
	 * - (bool)   hidden  is object hidden. optionally
4663
	 * - (string) alias   for symlinks - link target path relative to root path. optionally
4664
	 * - (string) target  for symlinks - link target path. optionally
4665
	 *
4666
	 * If file does not exists - returns empty array or false.
4667
	 *
4668
	 * @param  string  $path    file path 
4669
	 * @return array|false
4670
	 * @author Dmitry (dio) Levashov
4671
	 **/
4672
	abstract protected function _stat($path);
4673
	
4674
4675
	/***************** file stat ********************/
4676
4677
		
4678
	/**
4679
	 * Return true if path is dir and has at least one childs directory
4680
	 *
4681
	 * @param  string  $path  dir path
4682
	 * @return bool
4683
	 * @author Dmitry (dio) Levashov
4684
	 **/
4685
	abstract protected function _subdirs($path);
4686
	
4687
	/**
4688
	 * Return object width and height
4689
	 * Ususaly used for images, but can be realize for video etc...
4690
	 *
4691
	 * @param  string  $path  file path
4692
	 * @param  string  $mime  file mime type
4693
	 * @return string
4694
	 * @author Dmitry (dio) Levashov
4695
	 **/
4696
	abstract protected function _dimensions($path, $mime);
4697
	
4698
	/******************** file/dir content *********************/
4699
4700
	/**
4701
	 * Return files list in directory
4702
	 *
4703
	 * @param  string  $path  dir path
4704
	 * @return array
4705
	 * @author Dmitry (dio) Levashov
4706
	 **/
4707
	abstract protected function _scandir($path);
4708
	
4709
	/**
4710
	 * Open file and return file pointer
4711
	 *
4712
	 * @param  string  $path  file path
4713
	 * @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...
4714
	 * @return resource|false
4715
	 * @author Dmitry (dio) Levashov
4716
	 **/
4717
	abstract protected function _fopen($path, $mode="rb");
4718
	
4719
	/**
4720
	 * Close opened file
4721
	 * 
4722
	 * @param  resource  $fp    file pointer
4723
	 * @param  string    $path  file path
4724
	 * @return bool
4725
	 * @author Dmitry (dio) Levashov
4726
	 **/
4727
	abstract protected function _fclose($fp, $path='');
4728
	
4729
	/********************  file/dir manipulations *************************/
4730
	
4731
	/**
4732
	 * Create dir and return created dir path or false on failed
4733
	 *
4734
	 * @param  string  $path  parent dir path
4735
	 * @param string  $name  new directory name
4736
	 * @return string|bool
4737
	 * @author Dmitry (dio) Levashov
4738
	 **/
4739
	abstract protected function _mkdir($path, $name);
4740
	
4741
	/**
4742
	 * Create file and return it's path or false on failed
4743
	 *
4744
	 * @param  string  $path  parent dir path
4745
	 * @param string  $name  new file name
4746
	 * @return string|bool
4747
	 * @author Dmitry (dio) Levashov
4748
	 **/
4749
	abstract protected function _mkfile($path, $name);
4750
	
4751
	/**
4752
	 * Create symlink
4753
	 *
4754
	 * @param  string  $source     file to link to
4755
	 * @param  string  $targetDir  folder to create link in
4756
	 * @param  string  $name       symlink name
4757
	 * @return bool
4758
	 * @author Dmitry (dio) Levashov
4759
	 **/
4760
	abstract protected function _symlink($source, $targetDir, $name);
4761
	
4762
	/**
4763
	 * Copy file into another file (only inside one volume)
4764
	 *
4765
	 * @param  string  $source  source file path
4766
	 * @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...
4767
	 * @param  string  $name    file name
4768
	 * @return bool
4769
	 * @author Dmitry (dio) Levashov
4770
	 **/
4771
	abstract protected function _copy($source, $targetDir, $name);
4772
	
4773
	/**
4774
	 * Move file into another parent dir.
4775
	 * Return new file path or false.
4776
	 *
4777
	 * @param  string  $source  source file path
4778
	 * @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...
4779
	 * @param  string  $name    file name
4780
	 * @return string|bool
4781
	 * @author Dmitry (dio) Levashov
4782
	 **/
4783
	abstract protected function _move($source, $targetDir, $name);
4784
	
4785
	/**
4786
	 * Remove file
4787
	 *
4788
	 * @param  string  $path  file path
4789
	 * @return bool
4790
	 * @author Dmitry (dio) Levashov
4791
	 **/
4792
	abstract protected function _unlink($path);
4793
4794
	/**
4795
	 * Remove dir
4796
	 *
4797
	 * @param  string  $path  dir path
4798
	 * @return bool
4799
	 * @author Dmitry (dio) Levashov
4800
	 **/
4801
	abstract protected function _rmdir($path);
4802
4803
	/**
4804
	 * Create new file and write into it from file pointer.
4805
	 * Return new file path or false on error.
4806
	 *
4807
	 * @param  resource  $fp   file pointer
4808
	 * @param  string    $dir  target dir path
4809
	 * @param  string    $name file name
4810
	 * @param  array     $stat file stat (required by some virtual fs)
4811
	 * @return bool|string
4812
	 * @author Dmitry (dio) Levashov
4813
	 **/
4814
	abstract protected function _save($fp, $dir, $name, $stat);
4815
	
4816
	/**
4817
	 * Get file contents
4818
	 *
4819
	 * @param  string  $path  file path
4820
	 * @return string|false
4821
	 * @author Dmitry (dio) Levashov
4822
	 **/
4823
	abstract protected function _getContents($path);
4824
	
4825
	/**
4826
	 * Write a string to a file
4827
	 *
4828
	 * @param  string  $path     file path
4829
	 * @param  string  $content  new file content
4830
	 * @return bool
4831
	 * @author Dmitry (dio) Levashov
4832
	 **/
4833
	abstract protected function _filePutContents($path, $content);
4834
4835
	/**
4836
	 * Extract files from archive
4837
	 *
4838
	 * @param  string  $path file path
4839
	 * @param  array   $arc  archiver options
4840
	 * @return bool
4841
	 * @author Dmitry (dio) Levashov, 
4842
	 * @author Alexey Sukhotin
4843
	 **/
4844
	abstract protected function _extract($path, $arc);
4845
4846
	/**
4847
	 * Create archive and return its path
4848
	 *
4849
	 * @param  string  $dir    target dir
4850
	 * @param  array   $files  files names list
4851
	 * @param  string  $name   archive name
4852
	 * @param  array   $arc    archiver options
4853
	 * @return string|bool
4854
	 * @author Dmitry (dio) Levashov, 
4855
	 * @author Alexey Sukhotin
4856
	 **/
4857
	abstract protected function _archive($dir, $files, $name, $arc);
4858
4859
	/**
4860
	 * Detect available archivers
4861
	 *
4862
	 * @return void
4863
	 * @author Dmitry (dio) Levashov, 
4864
	 * @author Alexey Sukhotin
4865
	 **/
4866
	abstract protected function _checkArchivers();
4867
4868
	/**
4869
	 * Change file mode (chmod)
4870
	 *
4871
	 * @param  string  $path  file path
4872
	 * @param  string  $mode  octal string such as '0755'
4873
	 * @return bool
4874
	 * @author David Bartle,
4875
	 **/
4876
	abstract protected function _chmod($path, $mode);
4877
4878
	
4879
} // END class
4880