Completed
Push — 2.x ( 89625c...d1b1af )
by Naoki
03:16
created

elFinderVolumeDriver::stat()   C

Complexity

Conditions 12
Paths 31

Size

Total Lines 27
Code Lines 18

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 27
rs 5.1612
cc 12
eloc 18
nc 31
nop 1

How to fix   Complexity   

Long Method

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

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

Commonly applied refactorings include:

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

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

namespace YourVendor;

class YourClass { }

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

Loading history...
15
	
16
	/**
17
	 * Request args
18
	 * $_POST or $_GET values
19
	 * 
20
	 * @var array
21
	 */
22
	protected $ARGS = array();
23
	
24
	/**
25
	 * Driver id
26
	 * Must be started from letter and contains [a-z0-9]
27
	 * Used as part of volume id
28
	 *
29
	 * @var string
30
	 **/
31
	protected $driverId = 'a';
32
	
33
	/**
34
	 * Volume id - used as prefix for files hashes
35
	 *
36
	 * @var string
37
	 **/
38
	protected $id = '';
39
	
40
	/**
41
	 * Flag - volume "mounted" and available
42
	 *
43
	 * @var bool
44
	 **/
45
	protected $mounted = false;
46
	
47
	/**
48
	 * Root directory path
49
	 *
50
	 * @var string
51
	 **/
52
	protected $root = '';
53
	
54
	/**
55
	 * Root basename | alias
56
	 *
57
	 * @var string
58
	 **/
59
	protected $rootName = '';
60
	
61
	/**
62
	 * Default directory to open
63
	 *
64
	 * @var string
65
	 **/
66
	protected $startPath = '';
67
	
68
	/**
69
	 * Base URL
70
	 *
71
	 * @var string
72
	 **/
73
	protected $URL = '';
74
	
75
	/**
76
	 * Thumbnails dir path
77
	 *
78
	 * @var string
79
	 **/
80
	protected $tmbPath = '';
81
	
82
	/**
83
	 * Is thumbnails dir writable
84
	 *
85
	 * @var bool
86
	 **/
87
	protected $tmbPathWritable = false;
88
	
89
	/**
90
	 * Thumbnails base URL
91
	 *
92
	 * @var string
93
	 **/
94
	protected $tmbURL = '';
95
	
96
	/**
97
	 * Thumbnails size in px
98
	 *
99
	 * @var int
100
	 **/
101
	protected $tmbSize = 48;
102
	
103
	/**
104
	 * Image manipulation lib name
105
	 * auto|imagick|mogtify|gd
106
	 *
107
	 * @var string
108
	 **/
109
	protected $imgLib = 'auto';
110
	
111
	/**
112
	 * Library to crypt files name
113
	 *
114
	 * @var string
115
	 **/
116
	protected $cryptLib = '';
117
	
118
	/**
119
	 * Archivers config
120
	 *
121
	 * @var array
122
	 **/
123
	protected $archivers = array(
124
		'create'  => array(),
125
		'extract' => array()
126
	);
127
	
128
	/**
129
	 * Server character encoding
130
	 *
131
	 * @var string or null
132
	 **/
133
	protected $encoding = null;
134
	
135
	/**
136
	 * How many subdirs levels return for tree
137
	 *
138
	 * @var int
139
	 **/
140
	protected $treeDeep = 1;
141
	
142
	/**
143
	 * Errors from last failed action
144
	 *
145
	 * @var array
146
	 **/
147
	protected $error = array();
148
	
149
	/**
150
	 * Today 24:00 timestamp
151
	 *
152
	 * @var int
153
	 **/
154
	protected $today = 0;
155
	
156
	/**
157
	 * Yesterday 24:00 timestamp
158
	 *
159
	 * @var int
160
	 **/
161
	protected $yesterday = 0;
162
	
163
	/**
164
	 * Force make dirctory on extract
165
	 *
166
	 * @var int
167
	 **/
168
	protected $extractToNewdir = 'auto';
169
	
170
	/**
171
	 * Object configuration
172
	 *
173
	 * @var array
174
	 **/
175
	protected $options = array(
176
		'id'              => '',
177
		// root directory path
178
		'path'            => '',
179
		// open this path on initial request instead of root path
180
		'startPath'       => '',
181
		// how many subdirs levels return per request
182
		'treeDeep'        => 1,
183
		// root url, not set to disable sending URL to client (replacement for old "fileURL" option)
184
		'URL'             => '',
185
		// directory separator. required by client to show paths correctly
186
		'separator'       => DIRECTORY_SEPARATOR,
187
		// Server character encoding (default is '': UTF-8)
188
		'encoding'        => '',
189
		// for convert character encoding (default is '': Not change locale)
190
		'locale'          => '',
191
		// URL of volume icon (16x16 pixel image file)
192
		'icon'            => '',
193
		// CSS Class of volume root in tree
194
		'rootCssClass'    => '',
195
		// library to crypt/uncrypt files names (not implemented)
196
		'cryptLib'        => '',
197
		// how to detect files mimetypes. (auto/internal/finfo/mime_content_type)
198
		'mimeDetect'      => 'auto',
199
		// mime.types file path (for mimeDetect==internal)
200
		'mimefile'        => '',
201
		// mime type normalize map : Array '[ext]:[detected mime type]' => '[normalized mime]'
202
		'mimeMap'         => array(
203
		                     'md:application/x-genesis-rom' => 'text/x-markdown',
204
		                     'md:text/plain'                => 'text/x-markdown',
205
		                     'markdown:text/plain'          => 'text/x-markdown',
206
		                     'css:text/x-asm'               => 'text/css'
207
		                    ),
208
		// MIME regex of send HTTP header "Content-Disposition: inline"
209
		// '.' is allow inline of all of MIME types
210
		// '$^' is not allow inline of all of MIME types
211
		'dispInlineRegex' => '^(?:(?:image|text)|application/x-shockwave-flash$)',
212
		// directory for thumbnails
213
		'tmbPath'         => '.tmb',
214
		// mode to create thumbnails dir
215
		'tmbPathMode'     => 0777,
216
		// thumbnails dir URL. Set it if store thumbnails outside root directory
217
		'tmbURL'          => '',
218
		// thumbnails size (px)
219
		'tmbSize'         => 48,
220
		// thumbnails crop (true - crop, false - scale image to fit thumbnail size)
221
		'tmbCrop'         => true,
222
		// thumbnails background color (hex #rrggbb or 'transparent')
223
		'tmbBgColor'      => '#ffffff',
224
		// image manipulations library
225
		'imgLib'          => 'auto',
226
		// on paste file -  if true - old file will be replaced with new one, if false new file get name - original_name-number.ext
227
		'copyOverwrite'   => true,
228
		// if true - join new and old directories content on paste
229
		'copyJoin'        => true,
230
		// on upload -  if true - old file will be replaced with new one, if false new file get name - original_name-number.ext
231
		'uploadOverwrite' => true,
232
		// mimetypes allowed to upload
233
		'uploadAllow'     => array(),
234
		// mimetypes not allowed to upload
235
		'uploadDeny'      => array(),
236
		// order to proccess uploadAllow and uploadDeny options
237
		'uploadOrder'     => array('deny', 'allow'),
238
		// maximum upload file size. NOTE - this is size for every uploaded files
239
		'uploadMaxSize'   => 0,
240
		// files dates format
241
		'dateFormat'      => 'j M Y H:i',
242
		// files time format
243
		'timeFormat'      => 'H:i',
244
		// if true - every folder will be check for children folders, otherwise all folders will be marked as having subfolders
245
		'checkSubfolders' => true,
246
		// allow to copy from this volume to other ones?
247
		'copyFrom'        => true,
248
		// allow to copy from other volumes to this one?
249
		'copyTo'          => true,
250
		// list of commands disabled on this root
251
		'disabled'        => array(),
252
		// enable file owner, group & mode info, `false` to inactivate "chmod" command.
253
		'statOwner'       => false,
254
		// allow exec chmod of read-only files
255
		'allowChmodReadOnly' => false,
256
		// regexp or function name to validate new file name
257
		'acceptedName'    => '/^[^\.].*/', //<-- DONT touch this! Use constructor options to overwrite it!
258
		// function/class method to control files permissions
259
		'accessControl'   => null,
260
		// some data required by access control
261
		'accessControlData' => null,
262
		// default permissions.
263
		'defaults'     => array(
264
			'read'   => true,
265
			'write'  => true,
266
			'locked' => false,
267
			'hidden' => false
268
		),
269
		// files attributes
270
		'attributes'   => array(),
271
		// Allowed archive's mimetypes to create. Leave empty for all available types.
272
		'archiveMimes' => array(),
273
		// Manual config for archivers. See example below. Leave empty for auto detect
274
		'archivers'    => array(),
275
		// plugin settings
276
		'plugin'       => array(),
277
		// required to fix bug on macos
278
		'utf8fix'      => false,
279
		 //                           й                 ё              Й               Ё              Ø         Å
280
		'utf8patterns' => array("\u0438\u0306", "\u0435\u0308", "\u0418\u0306", "\u0415\u0308", "\u00d8A", "\u030a"),
281
		'utf8replace'  => array("\u0439",        "\u0451",       "\u0419",       "\u0401",       "\u00d8", "\u00c5")
282
	);
283
284
	/**
285
	 * Defaults permissions
286
	 *
287
	 * @var array
288
	 **/
289
	protected $defaults = array(
290
		'read'   => true,
291
		'write'  => true,
292
		'locked' => false,
293
		'hidden' => false
294
	);
295
	
296
	/**
297
	 * Access control function/class
298
	 *
299
	 * @var mixed
300
	 **/
301
	protected $attributes = array();
302
	
303
	/**
304
	 * Access control function/class
305
	 *
306
	 * @var mixed
307
	 **/
308
	protected $access = null;
309
	
310
	/**
311
	 * Mime types allowed to upload
312
	 *
313
	 * @var array
314
	 **/
315
	protected $uploadAllow = array();
316
	
317
	/**
318
	 * Mime types denied to upload
319
	 *
320
	 * @var array
321
	 **/
322
	protected $uploadDeny = array();
323
	
324
	/**
325
	 * Order to validate uploadAllow and uploadDeny
326
	 *
327
	 * @var array
328
	 **/
329
	protected $uploadOrder = array();
330
	
331
	/**
332
	 * Maximum allowed upload file size.
333
	 * Set as number or string with unit - "10M", "500K", "1G"
334
	 *
335
	 * @var int|string
336
	 **/
337
	protected $uploadMaxSize = 0;
338
	
339
	/**
340
	 * Mimetype detect method
341
	 *
342
	 * @var string
343
	 **/
344
	protected $mimeDetect = 'auto';
345
	
346
	/**
347
	 * Flag - mimetypes from externail file was loaded
348
	 *
349
	 * @var bool
350
	 **/
351
	private static $mimetypesLoaded = false;
352
	
353
	/**
354
	 * Finfo object for mimeDetect == 'finfo'
355
	 *
356
	 * @var object
357
	 **/
358
	protected $finfo = null;
359
	
360
	/**
361
	 * List of disabled client's commands
362
	 *
363
	 * @var array
364
	 **/
365
	protected $disabled = array();
366
	
367
	/**
368
	 * default extensions/mimetypes for mimeDetect == 'internal' 
369
	 *
370
	 * @var array
371
	 **/
372
	protected static $mimetypes = array(
373
		// applications
374
		'ai'    => 'application/postscript',
375
		'eps'   => 'application/postscript',
376
		'exe'   => 'application/x-executable',
377
		'doc'   => 'application/vnd.ms-word',
378
		'xls'   => 'application/vnd.ms-excel',
379
		'ppt'   => 'application/vnd.ms-powerpoint',
380
		'pps'   => 'application/vnd.ms-powerpoint',
381
		'pdf'   => 'application/pdf',
382
		'xml'   => 'application/xml',
383
		'swf'   => 'application/x-shockwave-flash',
384
		'torrent' => 'application/x-bittorrent',
385
		'jar'   => 'application/x-jar',
386
		// open office (finfo detect as application/zip)
387
		'odt'   => 'application/vnd.oasis.opendocument.text',
388
		'ott'   => 'application/vnd.oasis.opendocument.text-template',
389
		'oth'   => 'application/vnd.oasis.opendocument.text-web',
390
		'odm'   => 'application/vnd.oasis.opendocument.text-master',
391
		'odg'   => 'application/vnd.oasis.opendocument.graphics',
392
		'otg'   => 'application/vnd.oasis.opendocument.graphics-template',
393
		'odp'   => 'application/vnd.oasis.opendocument.presentation',
394
		'otp'   => 'application/vnd.oasis.opendocument.presentation-template',
395
		'ods'   => 'application/vnd.oasis.opendocument.spreadsheet',
396
		'ots'   => 'application/vnd.oasis.opendocument.spreadsheet-template',
397
		'odc'   => 'application/vnd.oasis.opendocument.chart',
398
		'odf'   => 'application/vnd.oasis.opendocument.formula',
399
		'odb'   => 'application/vnd.oasis.opendocument.database',
400
		'odi'   => 'application/vnd.oasis.opendocument.image',
401
		'oxt'   => 'application/vnd.openofficeorg.extension',
402
		// MS office 2007 (finfo detect as application/zip)
403
		'docx'  => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
404
		'docm'  => 'application/vnd.ms-word.document.macroEnabled.12',
405
		'dotx'  => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
406
		'dotm'  => 'application/vnd.ms-word.template.macroEnabled.12',
407
		'xlsx'  => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
408
		'xlsm'  => 'application/vnd.ms-excel.sheet.macroEnabled.12',
409
		'xltx'  => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
410
		'xltm'  => 'application/vnd.ms-excel.template.macroEnabled.12',
411
		'xlsb'  => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
412
		'xlam'  => 'application/vnd.ms-excel.addin.macroEnabled.12',
413
		'pptx'  => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
414
		'pptm'  => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
415
		'ppsx'  => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
416
		'ppsm'  => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12',
417
		'potx'  => 'application/vnd.openxmlformats-officedocument.presentationml.template',
418
		'potm'  => 'application/vnd.ms-powerpoint.template.macroEnabled.12',
419
		'ppam'  => 'application/vnd.ms-powerpoint.addin.macroEnabled.12',
420
		'sldx'  => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
421
		'sldm'  => 'application/vnd.ms-powerpoint.slide.macroEnabled.12',
422
		// archives
423
		'gz'    => 'application/x-gzip',
424
		'tgz'   => 'application/x-gzip',
425
		'bz'    => 'application/x-bzip2',
426
		'bz2'   => 'application/x-bzip2',
427
		'tbz'   => 'application/x-bzip2',
428
		'xz'    => 'application/x-xz',
429
		'zip'   => 'application/zip',
430
		'rar'   => 'application/x-rar',
431
		'tar'   => 'application/x-tar',
432
		'7z'    => 'application/x-7z-compressed',
433
		// texts
434
		'txt'   => 'text/plain',
435
		'php'   => 'text/x-php',
436
		'html'  => 'text/html',
437
		'htm'   => 'text/html',
438
		'js'    => 'text/javascript',
439
		'css'   => 'text/css',
440
		'rtf'   => 'text/rtf',
441
		'rtfd'  => 'text/rtfd',
442
		'py'    => 'text/x-python',
443
		'java'  => 'text/x-java-source',
444
		'rb'    => 'text/x-ruby',
445
		'sh'    => 'text/x-shellscript',
446
		'pl'    => 'text/x-perl',
447
		'xml'   => 'text/xml',
448
		'sql'   => 'text/x-sql',
449
		'c'     => 'text/x-csrc',
450
		'h'     => 'text/x-chdr',
451
		'cpp'   => 'text/x-c++src',
452
		'hh'    => 'text/x-c++hdr',
453
		'log'   => 'text/plain',
454
		'csv'   => 'text/x-comma-separated-values',
455
		'md'    => 'text/x-markdown',
456
		'markdown' => 'text/x-markdown',
457
		// images
458
		'bmp'   => 'image/x-ms-bmp',
459
		'jpg'   => 'image/jpeg',
460
		'jpeg'  => 'image/jpeg',
461
		'gif'   => 'image/gif',
462
		'png'   => 'image/png',
463
		'tif'   => 'image/tiff',
464
		'tiff'  => 'image/tiff',
465
		'tga'   => 'image/x-targa',
466
		'psd'   => 'image/vnd.adobe.photoshop',
467
		'ai'    => 'image/vnd.adobe.photoshop',
468
		'xbm'   => 'image/xbm',
469
		'pxm'   => 'image/pxm',
470
		//audio
471
		'mp3'   => 'audio/mpeg',
472
		'mid'   => 'audio/midi',
473
		'ogg'   => 'audio/ogg',
474
		'oga'   => 'audio/ogg',
475
		'm4a'   => 'audio/x-m4a',
476
		'wav'   => 'audio/wav',
477
		'wma'   => 'audio/x-ms-wma',
478
		// video
479
		'avi'   => 'video/x-msvideo',
480
		'dv'    => 'video/x-dv',
481
		'mp4'   => 'video/mp4',
482
		'mpeg'  => 'video/mpeg',
483
		'mpg'   => 'video/mpeg',
484
		'mov'   => 'video/quicktime',
485
		'wm'    => 'video/x-ms-wmv',
486
		'flv'   => 'video/x-flv',
487
		'mkv'   => 'video/x-matroska',
488
		'webm'  => 'video/webm',
489
		'ogv'   => 'video/ogg',
490
		'ogm'   => 'video/ogg'
491
		);
492
	
493
	/**
494
	 * Directory separator - required by client
495
	 *
496
	 * @var string
497
	 **/
498
	protected $separator = DIRECTORY_SEPARATOR;
499
	
500
	/**
501
	 * System Root path (Unix like: '/', Windows: '\', 'C:\' or 'D:\'...)
502
	 *
503
	 * @var string
504
	 **/
505
	protected $systemRoot = DIRECTORY_SEPARATOR;
506
	
507
	/**
508
	 * Mimetypes allowed to display
509
	 *
510
	 * @var array
511
	 **/
512
	protected $onlyMimes = array();
513
	
514
	/**
515
	 * Store files moved or overwrited files info
516
	 *
517
	 * @var array
518
	 **/
519
	protected $removed = array();
520
	
521
	/**
522
	 * Cache storage
523
	 *
524
	 * @var array
525
	 **/
526
	protected $cache = array();
527
	
528
	/**
529
	 * Cache by folders
530
	 *
531
	 * @var array
532
	 **/
533
	protected $dirsCache = array();
534
	
535
	/**
536
	 * Cache for subdirsCE()
537
	 * 
538
	 * @var array
539
	 */
540
	protected $subdirsCache = array();
541
	
542
	/**
543
	 * Reference of $_SESSION[elFinder::$sessionCacheKey][$this->id]
544
	 * 
545
	 * @var array
546
	 */
547
	protected $sessionCache;
548
	
549
	/*********************************************************************/
550
	/*                            INITIALIZATION                         */
551
	/*********************************************************************/
552
	
553
	/**
554
	 * Prepare driver before mount volume.
555
	 * Return true if volume is ready.
556
	 *
557
	 * @return bool
558
	 * @author Dmitry (dio) Levashov
559
	 **/
560
	protected function init() {
561
		return true;
562
	}	
563
		
564
	/**
565
	 * Configure after successfull mount.
566
	 * By default set thumbnails path and image manipulation library.
567
	 *
568
	 * @return void
569
	 * @author Dmitry (dio) Levashov
570
	 **/
571
	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...
572
		// set ARGS
573
		$this->ARGS = $_SERVER['REQUEST_METHOD'] === 'POST'? $_POST : $_GET;
574
		// set thumbnails path
575
		$path = $this->options['tmbPath'];
576
		if ($path) {
577 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...
578
				if (@mkdir($path)) {
579
					chmod($path, $this->options['tmbPathMode']);
580
				} else {
581
					$path = '';
582
				}
583
			} 
584
			
585
			if (is_dir($path) && is_readable($path)) {
586
				$this->tmbPath = $path;
587
				$this->tmbPathWritable = is_writable($path);
588
			}
589
		}
590
591
		// set image manipulation library
592
		$type = preg_match('/^(imagick|gd|auto)$/i', $this->options['imgLib'])
593
			? strtolower($this->options['imgLib'])
594
			: 'auto';
595
596
		if (($type == 'imagick' || $type == 'auto') && extension_loaded('imagick')) {
597
			$this->imgLib = 'imagick';
598
		} else {
599
			$this->imgLib = function_exists('gd_info') ? 'gd' : '';
600
		}
601
		
602
		// check 'statOwner' for command `chmod`
603
		if (empty($this->options['statOwner'])) {
604
			$this->disabled[] ='chmod';
605
		}
606
		
607
		// check 'mimeMap'
608
		if (!is_array($this->options['mimeMap'])) {
609
			$this->options['mimeMap'] = array();
610
		}
611
	}
612
	
613
	
614
	/*********************************************************************/
615
	/*                              PUBLIC API                           */
616
	/*********************************************************************/
617
	
618
	/**
619
	 * Return driver id. Used as a part of volume id.
620
	 *
621
	 * @return string
622
	 * @author Dmitry (dio) Levashov
623
	 **/
624
	public function driverId() {
625
		return $this->driverId;
626
	}
627
	
628
	/**
629
	 * Return volume id
630
	 *
631
	 * @return string
632
	 * @author Dmitry (dio) Levashov
633
	 **/
634
	public function id() {
635
		return $this->id;
636
	}
637
		
638
	/**
639
	 * Return debug info for client
640
	 *
641
	 * @return array
642
	 * @author Dmitry (dio) Levashov
643
	 **/
644
	public function debug() {
645
		return array(
646
			'id'         => $this->id(),
647
			'name'       => strtolower(substr(get_class($this), strlen('elfinderdriver'))),
648
			'mimeDetect' => $this->mimeDetect,
649
			'imgLib'     => $this->imgLib
650
		);
651
	}
652
653
	/**
654
	 * chmod a file or folder
655
	 *
656
	 * @param  string   $hash    file or folder hash to chmod
657
	 * @param  string   $mode    octal string representing new permissions
658
	 * @return array|false
659
	 * @author David Bartle
660
	 **/
661
	public function chmod($hash, $mode) {
662
		if ($this->commandDisabled('chmod')) {
663
			return $this->setError(elFinder::ERROR_PERM_DENIED);
664
		}
665
666
		if (!($file = $this->file($hash))) {
667
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
668
		}
669
670
		if (!$this->options['allowChmodReadOnly']) {
671 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...
672
				return $this->setError(elFinder::ERROR_PERM_DENIED, $file['name']);
673
			}
674
		}
675
676
		$path = $this->decode($hash);
677
678 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...
679
			return $this->setError(elFinder::ERROR_PERM_DENIED, $file['name']);
680
		}
681
682
		$this->clearcache();
683
684
		if ($file = $this->stat($path)) {
685
			$files = array($file);
686
			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...
687
				foreach ($this->getScandir($path) as $stat) {
688
					if ($this->mimeAccepted($stat['mime'])) {
689
						$files[] = $stat;
690
					}
691
				}
692
			}
693
			return $files;
694
		} else {
695
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
696
		}
697
	}
698
	
699
	/**
700
	 * "Mount" volume.
701
	 * Return true if volume available for read or write, 
702
	 * false - otherwise
703
	 *
704
	 * @return bool
705
	 * @author Dmitry (dio) Levashov
706
	 * @author Alexey Sukhotin
707
	 **/
708
	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...
709
		if (!isset($opts['path']) || $opts['path'] === '') {
710
			return $this->setError('Path undefined.');;
711
		}
712
		
713
		$this->options = array_merge($this->options, $opts);
714
		$this->id = $this->driverId.(!empty($this->options['id']) ? $this->options['id'] : elFinder::$volumesCnt++).'_';
715
		$this->root = $this->normpathCE($this->options['path']);
716
		$this->separator = isset($this->options['separator']) ? $this->options['separator'] : DIRECTORY_SEPARATOR;
717
		$this->systemRoot = isset($this->options['systemRoot']) ? $this->options['systemRoot'] : $this->separator;
718
		
719
		// set server encoding
720
		if (!empty($this->options['encoding']) && strtoupper($this->options['encoding']) !== 'UTF-8') {
721
			$this->encoding = $this->options['encoding'];
722
		} else {
723
			$this->encoding = null;
724
		}
725
		
726
		$argInit = !empty($this->ARGS['init']);
727
		
728
		// session cache
729
		if ($argInit || ! isset($_SESSION[elFinder::$sessionCacheKey][$this->id])) {
730
			$_SESSION[elFinder::$sessionCacheKey][$this->id] = array();
731
		}
732
		$this->sessionCache = &$_SESSION[elFinder::$sessionCacheKey][$this->id];
733
		
734
		// default file attribute
735
		$this->defaults = array(
736
			'read'    => isset($this->options['defaults']['read'])  ? !!$this->options['defaults']['read']  : true,
737
			'write'   => isset($this->options['defaults']['write']) ? !!$this->options['defaults']['write'] : true,
738
			'locked'  => isset($this->options['defaults']['locked']) ? !!$this->options['defaults']['locked'] : false,
739
			'hidden'  => isset($this->options['defaults']['hidden']) ? !!$this->options['defaults']['hidden'] : false
740
		);
741
742
		// root attributes
743
		$this->attributes[] = array(
744
			'pattern' => '~^'.preg_quote(DIRECTORY_SEPARATOR).'$~',
745
			'locked'  => true,
746
			'hidden'  => false
747
		);
748
		// set files attributes
749 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...
750
			
751
			foreach ($this->options['attributes'] as $a) {
752
				// attributes must contain pattern and at least one rule
753
				if (!empty($a['pattern']) || count($a) > 1) {
754
					$this->attributes[] = $a;
755
				}
756
			}
757
		}
758
759
		if (!empty($this->options['accessControl']) && is_callable($this->options['accessControl'])) {
760
			$this->access = $this->options['accessControl'];
761
		}
762
		
763
		$this->today     = mktime(0,0,0, date('m'), date('d'), date('Y'));
764
		$this->yesterday = $this->today-86400;
765
		
766
		// 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...
767
		if (!$this->init()) {
768
			return false;
769
		}
770
		
771
		// check some options is arrays
772
		$this->uploadAllow = isset($this->options['uploadAllow']) && is_array($this->options['uploadAllow'])
773
			? $this->options['uploadAllow']
774
			: array();
775
			
776
		$this->uploadDeny = isset($this->options['uploadDeny']) && is_array($this->options['uploadDeny'])
777
			? $this->options['uploadDeny']
778
			: array();
779
780
		if (is_string($this->options['uploadOrder'])) { // telephat_mode on, compatibility with 1.x
781
			$parts = explode(',', isset($this->options['uploadOrder']) ? $this->options['uploadOrder'] : 'deny,allow');
782
			$this->uploadOrder = array(trim($parts[0]), trim($parts[1]));
783
		} else { // telephat_mode off
784
			$this->uploadOrder = $this->options['uploadOrder'];
785
		}
786
			
787
		if (!empty($this->options['uploadMaxSize'])) {
788
			$size = ''.$this->options['uploadMaxSize'];
789
			$unit = strtolower(substr($size, strlen($size) - 1));
790
			$n = 1;
791
			switch ($unit) {
792
				case 'k':
793
					$n = 1024;
794
					break;
795
				case 'm':
796
					$n = 1048576;
797
					break;
798
				case 'g':
799
					$n = 1073741824;
800
			}
801
			$this->uploadMaxSize = intval($size)*$n;
802
		}
803
		// Set maximum to PHP_INT_MAX
804
		if (!defined('PHP_INT_MAX')) {
805
			define('PHP_INT_MAX', 2147483647);
806
		}
807
		if ($this->uploadMaxSize < 1 || $this->uploadMaxSize > PHP_INT_MAX) {
808
			$this->uploadMaxSize = PHP_INT_MAX;
809
		}
810
		
811
		$this->disabled = isset($this->options['disabled']) && is_array($this->options['disabled'])
812
			? $this->options['disabled']
813
			: array();
814
		
815
		$this->cryptLib   = $this->options['cryptLib'];
816
		$this->mimeDetect = $this->options['mimeDetect'];
817
818
		// find available mimetype detect method
819
		$type = strtolower($this->options['mimeDetect']);
820
		$type = preg_match('/^(finfo|mime_content_type|internal|auto)$/i', $type) ? $type : 'auto';
821
		$regexp = '/text\/x\-(php|c\+\+)/';
822
	
823
		if (($type == 'finfo' || $type == 'auto') 
824
		&& class_exists('finfo', false)) {
825
			$tmpFileInfo = @explode(';', @finfo_file(finfo_open(FILEINFO_MIME), __FILE__));
826
		} else {
827
			$tmpFileInfo = false;
828
		}
829
	
830
		if ($tmpFileInfo && preg_match($regexp, array_shift($tmpFileInfo))) {
831
			$type = 'finfo';
832
			$this->finfo = finfo_open(FILEINFO_MIME);
833
		} elseif (($type == 'mime_content_type' || $type == 'auto') 
834
		&& function_exists('mime_content_type')
835
		&& 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...
836
			$type = 'mime_content_type';
837
		} else {
838
			$type = 'internal';
839
		}
840
		$this->mimeDetect = $type;
841
842
		// load mimes from external file for mimeDetect == 'internal'
843
		// based on Alexey Sukhotin idea and patch: http://elrte.org/redmine/issues/163
844
		// file must be in file directory or in parent one 
845
		if ($this->mimeDetect == 'internal' && !self::$mimetypesLoaded) {
846
			self::$mimetypesLoaded = true;
847
			$this->mimeDetect = 'internal';
848
			$file = false;
849
			if (!empty($this->options['mimefile']) && file_exists($this->options['mimefile'])) {
850
				$file = $this->options['mimefile'];
851
			} elseif (file_exists(dirname(__FILE__).DIRECTORY_SEPARATOR.'mime.types')) {
852
				$file = dirname(__FILE__).DIRECTORY_SEPARATOR.'mime.types';
853
			} elseif (file_exists(dirname(dirname(__FILE__)).DIRECTORY_SEPARATOR.'mime.types')) {
854
				$file = dirname(dirname(__FILE__)).DIRECTORY_SEPARATOR.'mime.types';
855
			}
856
857
			if ($file && file_exists($file)) {
858
				$mimecf = file($file);
859
860
				foreach ($mimecf as $line_num => $line) {
861
					if (!preg_match('/^\s*#/', $line)) {
862
						$mime = preg_split('/\s+/', $line, -1, PREG_SPLIT_NO_EMPTY);
863
						for ($i = 1, $size = count($mime); $i < $size ; $i++) {
864
							if (!isset(self::$mimetypes[$mime[$i]])) {
865
								self::$mimetypes[$mime[$i]] = $mime[0];
866
							}
867
						}
868
					}
869
				}
870
			}
871
		}
872
873
		$this->rootName = empty($this->options['alias']) ? $this->basenameCE($this->root) : $this->options['alias'];
874
875
		// This get's triggered if $this->root == '/' and alias is empty.
876
		// Maybe modify _basename instead?
877
		if ($this->rootName === '') $this->rootName = $this->separator;
878
879
		$root = $this->stat($this->root);
880
		
881
		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...
882
			return $this->setError('Root folder does not exists.');
883
		}
884
		if (!$root['read'] && !$root['write']) {
885
			return $this->setError('Root folder has not read and write permissions.');
886
		}
887
		
888
		// 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...
889
		
890
		if ($root['read']) {
891
			// check startPath - path to open by default instead of root
892
			if ($this->options['startPath']) {
893
				$start = $this->stat($this->options['startPath']);
894
				if (!empty($start)
895
				&& $start['mime'] == 'directory'
896
				&& $start['read']
897
				&& empty($start['hidden'])
898
				&& $this->inpathCE($this->options['startPath'], $this->root)) {
899
					$this->startPath = $this->options['startPath'];
900
					if (substr($this->startPath, -1, 1) == $this->options['separator']) {
901
						$this->startPath = substr($this->startPath, 0, -1);
902
					}
903
				}
904
			}
905
		} else {
906
			$this->options['URL']     = '';
907
			$this->options['tmbURL']  = '';
908
			$this->options['tmbPath'] = '';
909
			// read only volume
910
			array_unshift($this->attributes, array(
911
				'pattern' => '/.*/',
912
				'read'    => false
913
			));
914
		}
915
		$this->treeDeep = $this->options['treeDeep'] > 0 ? (int)$this->options['treeDeep'] : 1;
916
		$this->tmbSize  = $this->options['tmbSize'] > 0 ? (int)$this->options['tmbSize'] : 48;
917
		$this->URL      = $this->options['URL'];
918
		if ($this->URL && preg_match("|[^/?&=]$|", $this->URL)) {
919
			$this->URL .= '/';
920
		}
921
922
		$this->tmbURL   = !empty($this->options['tmbURL']) ? $this->options['tmbURL'] : '';
923
		if ($this->tmbURL && preg_match("|[^/?&=]$|", $this->tmbURL)) {
924
			$this->tmbURL .= '/';
925
		}
926
		
927
		$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...
928
			? $this->options['acceptedName']
929
			: '';
930
931
		$this->_checkArchivers();
932
		// manual control archive types to create
933 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...
934
			foreach ($this->archivers['create'] as $mime => $v) {
935
				if (!in_array($mime, $this->options['archiveMimes'])) {
936
					unset($this->archivers['create'][$mime]);
937
				}
938
			}
939
		}
940
		
941
		// manualy add archivers
942 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...
943
			foreach ($this->options['archivers']['create'] as $mime => $conf) {
944
				if (strpos($mime, 'application/') === 0 
945
				&& !empty($conf['cmd']) 
946
				&& isset($conf['argc']) 
947
				&& !empty($conf['ext'])
948
				&& !isset($this->archivers['create'][$mime])) {
949
					$this->archivers['create'][$mime] = $conf;
950
				}
951
			}
952
		}
953
		
954 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...
955
			foreach ($this->options['archivers']['extract'] as $mime => $conf) {
956
				if (strpos($mime, 'application/') === 0
957
				&& !empty($conf['cmd']) 
958
				&& isset($conf['argc']) 
959
				&& !empty($conf['ext'])
960
				&& !isset($this->archivers['extract'][$mime])) {
961
					$this->archivers['extract'][$mime] = $conf;
962
				}
963
			}
964
		}
965
966
		$this->configure();
967
		// 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...
968
		// 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...
969
		return $this->mounted = true;
970
	}
971
	
972
	/**
973
	 * Some "unmount" stuffs - may be required by virtual fs
974
	 *
975
	 * @return void
976
	 * @author Dmitry (dio) Levashov
977
	 **/
978
	public function umount() {
979
	}
980
	
981
	/**
982
	 * Return error message from last failed action
983
	 *
984
	 * @return array
985
	 * @author Dmitry (dio) Levashov
986
	 **/
987
	public function error() {
988
		return $this->error;
989
	}
990
	
991
	/**
992
	 * Return Extention/MIME Table (elFinderVolumeDriver::$mimetypes)
993
	 * 
994
	 * @return array
995
	 * @author Naoki Sawada
996
	 */
997
	public function getMimeTable() {
998
		return elFinderVolumeDriver::$mimetypes;
999
	}
1000
	
1001
	/**
1002
	 * Set mimetypes allowed to display to client
1003
	 *
1004
	 * @param  array  $mimes
1005
	 * @return void
1006
	 * @author Dmitry (dio) Levashov
1007
	 **/
1008
	public function setMimesFilter($mimes) {
1009
		if (is_array($mimes)) {
1010
			$this->onlyMimes = $mimes;
1011
		}
1012
	}
1013
	
1014
	/**
1015
	 * Return root folder hash
1016
	 *
1017
	 * @return string
1018
	 * @author Dmitry (dio) Levashov
1019
	 **/
1020
	public function root() {
1021
		return $this->encode($this->root);
1022
	}
1023
	
1024
	/**
1025
	 * Return target path hash
1026
	 * 
1027
	 * @param  string $path
1028
	 * @param  string $name
1029
	 * @author Naoki Sawada
1030
	 */
1031
	public function getHash($path, $name = '') {
1032
		if ($name !== '') {
1033
			$path = $this->joinPathCE($path, $name);
1034
		}
1035
		return $this->encode($path);
1036
	}
1037
	
1038
	/**
1039
	 * Return root or startPath hash
1040
	 *
1041
	 * @return string
1042
	 * @author Dmitry (dio) Levashov
1043
	 **/
1044
	public function defaultPath() {
1045
		return $this->encode($this->startPath ? $this->startPath : $this->root);
1046
	}
1047
		
1048
	/**
1049
	 * Return volume options required by client:
1050
	 *
1051
	 * @return array
1052
	 * @author Dmitry (dio) Levashov
1053
	 **/
1054
	public function options($hash) {
1055
		$create = $createext = array();
1056
		if (isset($this->archivers['create']) && is_array($this->archivers['create'])) {
1057
			foreach($this->archivers['create'] as $m => $v) {
1058
				$create[] = $m;
1059
				$createext[$m] = $v['ext'];
1060
			}
1061
		}
1062
		return array(
1063
			'path'            => $this->path($hash),
1064
			'url'             => $this->URL,
1065
			'tmbUrl'          => $this->tmbURL,
1066
			'disabled'        => array_merge(array_unique($this->disabled)), // `array_merge` for type array of JSON
1067
			'separator'       => $this->separator,
1068
			'copyOverwrite'   => intval($this->options['copyOverwrite']),
1069
			'uploadOverwrite' => intval($this->options['uploadOverwrite']),
1070
			'uploadMaxSize'   => intval($this->uploadMaxSize),
1071
			'dispInlineRegex' => $this->options['dispInlineRegex'],
1072
			'archivers'       => array(
1073
				'create'    => $create,
1074
				'extract'   => isset($this->archivers['extract']) && is_array($this->archivers['extract']) ? array_keys($this->archivers['extract']) : array(),
1075
				'createext' => $createext
1076
			),
1077
			'uiCmdMap'        => (isset($this->options['uiCmdMap']) && is_array($this->options['uiCmdMap']))? $this->options['uiCmdMap'] : array()
1078
		);
1079
	}
1080
	
1081
	/**
1082
	 * Get option value of this volume
1083
	 * 
1084
	 * @param string $name  target option name
1085
	 * @return NULL|mixed   target option value
1086
	 * @author Naoki Sawada
1087
	 */
1088
	public function getOption($name) {
1089
		return isset($this->options[$name])? $this->options[$name] : null;
1090
	}
1091
	
1092
	/**
1093
	 * Get plugin values of this options
1094
	 * 
1095
	 * @param string $name  Plugin name
1096
	 * @return NULL|array   Plugin values
1097
	 * @author Naoki Sawada
1098
	 */
1099
	public function getOptionsPlugin($name = '') {
1100
		if ($name) {
1101
			return isset($this->options['plugin'][$name])? $this->options['plugin'][$name] : array();
1102
		} else {
1103
			return $this->options['plugin'];
1104
		}
1105
	}
1106
	
1107
	/**
1108
	 * Return true if command disabled in options
1109
	 *
1110
	 * @param  string  $cmd  command name
1111
	 * @return bool
1112
	 * @author Dmitry (dio) Levashov
1113
	 **/
1114
	public function commandDisabled($cmd) {
1115
		return in_array($cmd, $this->disabled);
1116
	}
1117
	
1118
	/**
1119
	 * Return true if mime is required mimes list
1120
	 *
1121
	 * @param  string     $mime   mime type to check
1122
	 * @param  array      $mimes  allowed mime types list or not set to use client mimes list
1123
	 * @param  bool|null  $empty  what to return on empty list
1124
	 * @return bool|null
1125
	 * @author Dmitry (dio) Levashov
1126
	 * @author Troex Nevelin
1127
	 **/
1128
	public function mimeAccepted($mime, $mimes = null, $empty = true) {
1129
		$mimes = is_array($mimes) ? $mimes : $this->onlyMimes;
1130
		if (empty($mimes)) {
1131
			return $empty;
1132
		}
1133
		return $mime == 'directory'
1134
			|| in_array('all', $mimes)
1135
			|| in_array('All', $mimes)
1136
			|| in_array($mime, $mimes)
1137
			|| in_array(substr($mime, 0, strpos($mime, '/')), $mimes);
1138
	}
1139
	
1140
	/**
1141
	 * Return true if voume is readable.
1142
	 *
1143
	 * @return bool
1144
	 * @author Dmitry (dio) Levashov
1145
	 **/
1146
	public function isReadable() {
1147
		$stat = $this->stat($this->root);
1148
		return $stat['read'];
1149
	}
1150
	
1151
	/**
1152
	 * Return true if copy from this volume allowed
1153
	 *
1154
	 * @return bool
1155
	 * @author Dmitry (dio) Levashov
1156
	 **/
1157
	public function copyFromAllowed() {
1158
		return !!$this->options['copyFrom'];
1159
	}
1160
	
1161
	/**
1162
	 * Return file path related to root with convert encoging
1163
	 *
1164
	 * @param  string   $hash  file hash
1165
	 * @return string
1166
	 * @author Dmitry (dio) Levashov
1167
	 **/
1168
	public function path($hash) {
1169
		return $this->convEncOut($this->_path($this->convEncIn($this->decode($hash))));
1170
	}
1171
	
1172
	/**
1173
	 * Return file real path if file exists
1174
	 *
1175
	 * @param  string  $hash  file hash
1176
	 * @return string
1177
	 * @author Dmitry (dio) Levashov
1178
	 **/
1179
	public function realpath($hash) {
1180
		$path = $this->decode($hash);
1181
		return $this->stat($path) ? $path : false;
1182
	}
1183
	
1184
	/**
1185
	 * Return list of moved/overwrited files
1186
	 *
1187
	 * @return array
1188
	 * @author Dmitry (dio) Levashov
1189
	 **/
1190
	public function removed() {
1191
		return $this->removed;
1192
	}
1193
	
1194
	/**
1195
	 * Clean removed files list
1196
	 *
1197
	 * @return void
1198
	 * @author Dmitry (dio) Levashov
1199
	 **/
1200
	public function resetRemoved() {
1201
		$this->removed = array();
1202
	}
1203
	
1204
	/**
1205
	 * Return file/dir hash or first founded child hash with required attr == $val
1206
	 *
1207
	 * @param  string   $hash  file hash
1208
	 * @param  string   $attr  attribute name
1209
	 * @param  bool     $val   attribute value
1210
	 * @return string|false
1211
	 * @author Dmitry (dio) Levashov
1212
	 **/
1213
	public function closest($hash, $attr, $val) {
1214
		return ($path = $this->closestByAttr($this->decode($hash), $attr, $val)) ? $this->encode($path) : false;
1215
	}
1216
	
1217
	/**
1218
	 * Return file info or false on error
1219
	 *
1220
	 * @param  string   $hash      file hash
1221
	 * @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...
1222
	 * @return array|false
1223
	 * @author Dmitry (dio) Levashov
1224
	 **/
1225
	public function file($hash) {
1226
		$path = $this->decode($hash);
1227
		
1228
		$file = $this->stat($path);
1229
		
1230
		if ($hash === $this->root()) {
1231
			$file['uiCmdMap'] = (isset($this->options['uiCmdMap']) && is_array($this->options['uiCmdMap']))? $this->options['uiCmdMap'] : array();
1232
			$file['disabled'] = array_merge(array_unique($this->disabled)); // `array_merge` for type array of JSON
1233
		}
1234
		
1235
		return ($file) ? $file : $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1236
	}
1237
	
1238
	/**
1239
	 * Return folder info
1240
	 *
1241
	 * @param  string   $hash  folder hash
1242
	 * @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...
1243
	 * @return array|false
1244
	 * @author Dmitry (dio) Levashov
1245
	 **/
1246
	public function dir($hash, $resolveLink=false) {
1247
		if (($dir = $this->file($hash)) == false) {
1248
			return $this->setError(elFinder::ERROR_DIR_NOT_FOUND);
1249
		}
1250
1251
		if ($resolveLink && !empty($dir['thash'])) {
1252
			$dir = $this->file($dir['thash']);
1253
		}
1254
		
1255
		return $dir && $dir['mime'] == 'directory' && empty($dir['hidden']) 
1256
			? $dir 
1257
			: $this->setError(elFinder::ERROR_NOT_DIR);
1258
	}
1259
	
1260
	/**
1261
	 * Return directory content or false on error
1262
	 *
1263
	 * @param  string   $hash   file hash
1264
	 * @return array|false
1265
	 * @author Dmitry (dio) Levashov
1266
	 **/
1267
	public function scandir($hash) {
1268
		if (($dir = $this->dir($hash)) == false) {
1269
			return false;
1270
		}
1271
		
1272
		return $dir['read']
1273
			? $this->getScandir($this->decode($hash))
1274
			: $this->setError(elFinder::ERROR_PERM_DENIED);
1275
	}
1276
1277
	/**
1278
	 * Return dir files names list
1279
	 * 
1280
	 * @param  string  $hash   file hash
1281
	 * @return array
1282
	 * @author Dmitry (dio) Levashov
1283
	 **/
1284
	public function ls($hash) {
1285 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...
1286
			return false;
1287
		}
1288
		
1289
		$list = array();
1290
		$path = $this->decode($hash);
1291
		
1292
		foreach ($this->getScandir($path) as $stat) {
1293
			if (empty($stat['hidden']) && $this->mimeAccepted($stat['mime'])) {
1294
				$list[] = $stat['name'];
1295
			}
1296
		}
1297
1298
		return $list;
1299
	}
1300
1301
	/**
1302
	 * Return subfolders for required folder or false on error
1303
	 *
1304
	 * @param  string   $hash  folder hash or empty string to get tree from root folder
1305
	 * @param  int      $deep  subdir deep
1306
	 * @param  string   $exclude  dir hash which subfolders must be exluded from result, required to not get stat twice on cwd subfolders
1307
	 * @return array|false
1308
	 * @author Dmitry (dio) Levashov
1309
	 **/
1310
	public function tree($hash='', $deep=0, $exclude='') {
1311
		$path = $hash ? $this->decode($hash) : $this->root;
1312
		
1313 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...
1314
			return false;
1315
		}
1316
		
1317
		$dirs = $this->gettree($path, $deep > 0 ? $deep -1 : $this->treeDeep-1, $exclude ? $this->decode($exclude) : null);
1318
		array_unshift($dirs, $dir);
1319
		return $dirs;
1320
	}
1321
	
1322
	/**
1323
	 * Return part of dirs tree from required dir up to root dir
1324
	 *
1325
	 * @param  string    $hash   directory hash
1326
	 * @param  bool|null $lineal only lineal parents
1327
	 * @return array
1328
	 * @author Dmitry (dio) Levashov
1329
	 **/
1330
	public function parents($hash, $lineal = false) {
1331
		if (($current = $this->dir($hash)) == false) {
1332
			return false;
1333
		}
1334
1335
		$path = $this->decode($hash);
1336
		$tree = array();
1337
		
1338
		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...
1339
			$path = $this->dirnameCE($path);
1340
			$stat = $this->stat($path);
1341
			if (!empty($stat['hidden']) || !$stat['read']) {
1342
				return false;
1343
			}
1344
			
1345
			array_unshift($tree, $stat);
1346
			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...
1347
				foreach ($this->gettree($path, 0) as $dir) {
1348
					if (!in_array($dir, $tree)) {
1349
						$tree[] = $dir;
1350
					}
1351
				}
1352
			}
1353
		}
1354
1355
		return $tree ? $tree : array($current);
1356
	}
1357
	
1358
	/**
1359
	 * Create thumbnail for required file and return its name of false on failed
1360
	 *
1361
	 * @return string|false
1362
	 * @author Dmitry (dio) Levashov
1363
	 **/
1364
	public function tmb($hash) {
1365
		$path = $this->decode($hash);
1366
		$stat = $this->stat($path);
1367
		
1368
		if (isset($stat['tmb'])) {
1369
			return $stat['tmb'] == "1" ? $this->createTmb($path, $stat) : $stat['tmb'];
1370
		}
1371
		return false;
1372
	}
1373
	
1374
	/**
1375
	 * Return file size / total directory size
1376
	 *
1377
	 * @param  string   file hash
1378
	 * @return int
1379
	 * @author Dmitry (dio) Levashov
1380
	 **/
1381
	public function size($hash) {
1382
		return $this->countSize($this->decode($hash));
1383
	}
1384
	
1385
	/**
1386
	 * Open file for reading and return file pointer
1387
	 *
1388
	 * @param  string   file hash
1389
	 * @return Resource
1390
	 * @author Dmitry (dio) Levashov
1391
	 **/
1392
	public function open($hash) {
1393 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...
1394
		|| $file['mime'] == 'directory') {
1395
			return false;
1396
		}
1397
		
1398
		return $this->fopenCE($this->decode($hash), 'rb');
1399
	}
1400
	
1401
	/**
1402
	 * Close file pointer
1403
	 *
1404
	 * @param  Resource  $fp   file pointer
1405
	 * @param  string    $hash file hash
1406
	 * @return void
1407
	 * @author Dmitry (dio) Levashov
1408
	 **/
1409
	public function close($fp, $hash) {
1410
		$this->fcloseCE($fp, $this->decode($hash));
1411
	}
1412
	
1413
	/**
1414
	 * Create directory and return dir info
1415
	 *
1416
	 * @param  string   $dsthash  destination directory hash
1417
	 * @param  string   $name directory name
1418
	 * @return array|false
1419
	 * @author Dmitry (dio) Levashov
1420
	 **/
1421
	public function mkdir($dsthash, $name) {
1422
		if ($this->commandDisabled('mkdir')) {
1423
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1424
		}
1425
		
1426
		if (!$this->nameAccepted($name)) {
1427
			return $this->setError(elFinder::ERROR_INVALID_NAME);
1428
		}
1429
		
1430
		if (($dir = $this->dir($dsthash)) == false) {
1431
			return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dsthash);
1432
		}
1433
		
1434
		$path = $this->decode($dsthash);
1435
		
1436
		if (!$dir['write'] || !$this->allowCreate($path, $name, true)) {
1437
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1438
		}
1439
		
1440
		$dst  = $this->joinPathCE($path, $name);
1441
		$stat = $this->stat($dst); 
1442
		if (!empty($stat)) { 
1443
			return $this->setError(elFinder::ERROR_EXISTS, $name);
1444
		}
1445
		$this->clearcache();
1446
		return ($path = $this->convEncOut($this->_mkdir($this->convEncIn($path), $this->convEncIn($name)))) ? $this->stat($path) : false;
1447
	}
1448
	
1449
	/**
1450
	 * Create empty file and return its info
1451
	 *
1452
	 * @param  string   $dst  destination directory
1453
	 * @param  string   $name file name
1454
	 * @return array|false
1455
	 * @author Dmitry (dio) Levashov
1456
	 **/
1457
	public function mkfile($dst, $name) {
1458
		if ($this->commandDisabled('mkfile')) {
1459
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1460
		}
1461
		
1462
		if (!$this->nameAccepted($name)) {
1463
			return $this->setError(elFinder::ERROR_INVALID_NAME);
1464
		}
1465
		
1466 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...
1467
			return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst);
1468
		}
1469
		
1470
		$path = $this->decode($dst);
1471
		
1472
		if (!$dir['write'] || !$this->allowCreate($path, $name, false)) {
1473
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1474
		}
1475
		
1476
		if ($this->stat($this->joinPathCE($path, $name))) {
1477
			return $this->setError(elFinder::ERROR_EXISTS, $name);
1478
		}
1479
		
1480
		$this->clearcache();
1481
		return ($path = $this->convEncOut($this->_mkfile($this->convEncIn($path), $this->convEncIn($name)))) ? $this->stat($path) : false;
1482
	}
1483
	
1484
	/**
1485
	 * Rename file and return file info
1486
	 *
1487
	 * @param  string  $hash  file hash
1488
	 * @param  string  $name  new file name
1489
	 * @return array|false
1490
	 * @author Dmitry (dio) Levashov
1491
	 **/
1492
	public function rename($hash, $name) {
1493
		if ($this->commandDisabled('rename')) {
1494
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1495
		}
1496
		
1497
		if (!$this->nameAccepted($name)) {
1498
			return $this->setError(elFinder::ERROR_INVALID_NAME, $name);
1499
		}
1500
		
1501
		$mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name);
1502 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...
1503
			return $this->setError(elFinder::ERROR_INVALID_NAME, $name);
1504
		}
1505
		
1506
		if (!($file = $this->file($hash))) {
1507
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1508
		}
1509
		
1510
		if ($name == $file['name']) {
1511
			return $file;
1512
		}
1513
		
1514
		if (!empty($file['locked'])) {
1515
			return $this->setError(elFinder::ERROR_LOCKED, $file['name']);
1516
		}
1517
		
1518
		$path = $this->decode($hash);
1519
		$dir  = $this->dirnameCE($path);
1520
		$stat = $this->stat($this->joinPathCE($dir, $name));
1521
		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...
1522
			return $this->setError(elFinder::ERROR_EXISTS, $name);
1523
		}
1524
		
1525 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...
1526
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1527
		}
1528
1529
		$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...
1530
1531
1532 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...
1533
			$this->clearcache();
1534
			return $this->stat($path);
1535
		}
1536
		return false;
1537
	}
1538
	
1539
	/**
1540
	 * Create file copy with suffix "copy number" and return its info
1541
	 *
1542
	 * @param  string   $hash    file hash
1543
	 * @param  string   $suffix  suffix to add to file name
1544
	 * @return array|false
1545
	 * @author Dmitry (dio) Levashov
1546
	 **/
1547
	public function duplicate($hash, $suffix='copy') {
1548
		if ($this->commandDisabled('duplicate')) {
1549
			return $this->setError(elFinder::ERROR_COPY, '#'.$hash, elFinder::ERROR_PERM_DENIED);
1550
		}
1551
		
1552
		if (($file = $this->file($hash)) == false) {
1553
			return $this->setError(elFinder::ERROR_COPY, elFinder::ERROR_FILE_NOT_FOUND);
1554
		}
1555
1556
		$path = $this->decode($hash);
1557
		$dir  = $this->dirnameCE($path);
1558
		$name = $this->uniqueName($dir, $this->basenameCE($path), ' '.$suffix.' ');
1559
1560 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...
1561
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1562
		}
1563
1564
		return ($path = $this->copy($path, $dir, $name)) == false
1565
			? false
1566
			: $this->stat($path);
0 ignored issues
show
Bug introduced by
It seems like $path defined by $this->copy($path, $dir, $name) on line 1564 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...
1567
	}
1568
	
1569
	/**
1570
	 * Save uploaded file. 
1571
	 * On success return array with new file stat and with removed file hash (if existed file was replaced)
1572
	 *
1573
	 * @param  Resource $fp      file pointer
1574
	 * @param  string   $dst     destination folder hash
1575
	 * @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...
1576
	 * @param  string   $tmpname file tmp name - required to detect mime type
1577
	 * @return array|false
1578
	 * @author Dmitry (dio) Levashov
1579
	 **/
1580
	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...
1581
		if ($this->commandDisabled('upload')) {
1582
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1583
		}
1584
		
1585 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...
1586
			return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst);
1587
		}
1588
1589
		if (!$dir['write']) {
1590
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1591
		}
1592
		
1593
		if (!$this->nameAccepted($name)) {
1594
			return $this->setError(elFinder::ERROR_INVALID_NAME);
1595
		}
1596
		
1597
		$mime = $this->mimetype($this->mimeDetect == 'internal' ? $name : $tmpname, $name);
1598
		$mimeByName = '';
1599
		if ($this->mimeDetect !== 'internal') {
1600
			$mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name);
1601
			if ($mime == 'unknown') {
1602
				$mime = $mimeByName;
1603
			}
1604
		}
1605
1606 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...
1607
			return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME);
1608
		}
1609
1610
		$tmpsize = sprintf('%u', filesize($tmpname));
1611
		if ($this->uploadMaxSize > 0 && $tmpsize > $this->uploadMaxSize) {
1612
			return $this->setError(elFinder::ERROR_UPLOAD_FILE_SIZE);
1613
		}
1614
1615
		$dstpath = $this->decode($dst);
1616
		$test    = $this->joinPathCE($dstpath, $name);
1617
		
1618
		$file = $this->stat($test);
1619
		$this->clearcache();
1620
		
1621
		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...
1622
			// check POST data `overwrite` for 3rd party uploader
1623
			$overwrite = isset($_POST['overwrite'])? (bool)$_POST['overwrite'] : $this->options['uploadOverwrite'];
1624
			if ($overwrite) {
1625 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...
1626
					return $this->setError(elFinder::ERROR_PERM_DENIED);
1627
				} elseif ($file['mime'] == 'directory') {
1628
					return $this->setError(elFinder::ERROR_NOT_REPLACE, $name);
1629
				} 
1630
				$this->remove($test);
1631
			} else {
1632
				$name = $this->uniqueName($dstpath, $name, '-', false);
1633
			}
1634
		}
1635
		
1636
		$stat = array(
1637
			'mime'   => $mime, 
1638
			'width'  => 0, 
1639
			'height' => 0, 
1640
			'size'   => $tmpsize);
1641
		
1642
		// $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...
1643
		if (strpos($mime, 'image') === 0 && ($s = getimagesize($tmpname))) {
1644
			$stat['width'] = $s[0];
1645
			$stat['height'] = $s[1];
1646
		}
1647
		// $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...
1648
		if (($path = $this->saveCE($fp, $dstpath, $name, $stat)) == false) {
1649
			return false;
1650
		}
1651
		
1652
		
1653
1654
		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 1648 can also be of type boolean; however, elFinderVolumeDriver::stat() does only seem to accept string, maybe add an additional type check?

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

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

    return array();
}

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

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

Loading history...
1655
	}
1656
	
1657
	/**
1658
	 * Paste files
1659
	 *
1660
	 * @param  Object  $volume  source volume
1661
	 * @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...
1662
	 * @param  string  $dst     destination dir hash
1663
	 * @param  bool    $rmSrc   remove source after copy?
1664
	 * @return array|false
1665
	 * @author Dmitry (dio) Levashov
1666
	 **/
1667
	public function paste($volume, $src, $dst, $rmSrc = false) {
1668
		$err = $rmSrc ? elFinder::ERROR_MOVE : elFinder::ERROR_COPY;
1669
		
1670
		if ($this->commandDisabled('paste')) {
1671
			return $this->setError($err, '#'.$src, elFinder::ERROR_PERM_DENIED);
1672
		}
1673
1674
		if (($file = $volume->file($src, $rmSrc)) == false) {
1675
			return $this->setError($err, '#'.$src, elFinder::ERROR_FILE_NOT_FOUND);
1676
		}
1677
1678
		$name = $file['name'];
1679
		$errpath = $volume->path($file['hash']);
1680
		
1681 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...
1682
			return $this->setError($err, $errpath, elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst);
1683
		}
1684
		
1685
		if (!$dir['write'] || !$file['read']) {
1686
			return $this->setError($err, $errpath, elFinder::ERROR_PERM_DENIED);
1687
		}
1688
1689
		$destination = $this->decode($dst);
1690
1691
		if (($test = $volume->closest($src, $rmSrc ? 'locked' : 'read', $rmSrc))) {
1692
			return $rmSrc
1693
				? $this->setError($err, $errpath, elFinder::ERROR_LOCKED, $volume->path($test))
1694
				: $this->setError($err, $errpath, !empty($file['thash'])? elFinder::ERROR_PERM_DENIED : elFinder::ERROR_MKOUTLINK);
1695
		}
1696
1697
		$test = $this->joinPathCE($destination, $name);
1698
		$stat = $this->stat($test);
1699
		$this->clearcache();
1700
		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...
1701
			if ($this->options['copyOverwrite']) {
1702
				// do not replace file with dir or dir with file
1703
				if (!$this->isSameType($file['mime'], $stat['mime'])) {
1704
					return $this->setError(elFinder::ERROR_NOT_REPLACE, $this->path($stat['hash']));
1705
				}
1706
				// existed file is not writable
1707
				if (!$stat['write']) {
1708
					return $this->setError($err, $errpath, elFinder::ERROR_PERM_DENIED);
1709
				}
1710
				// existed file locked or has locked child
1711
				if (($locked = $this->closestByAttr($test, 'locked', true))) {
1712
					$stat = $this->stat($locked);
1713
					return $this->setError(elFinder::ERROR_LOCKED, $this->path($stat['hash']));
1714
				}
1715
				// target is entity file of alias
1716
				if ($volume == $this && ($test == @$file['target'] || $test == $this->decode($src))) {
1717
					return $this->setError(elFinder::ERROR_REPLACE, $errpath);
1718
				}
1719
				// remove existed file
1720
				if (!$this->remove($test)) {
1721
					return $this->setError(elFinder::ERROR_REPLACE, $this->path($stat['hash']));
1722
				}
1723
			} else {
1724
				$name = $this->uniqueName($destination, $name, ' ', false);
1725
			}
1726
		}
1727
		
1728
		// copy/move inside current volume
1729
		if ($volume == $this) {
1730
			$source = $this->decode($src);
1731
			// do not copy into itself
1732
			if ($this->inpathCE($destination, $source)) {
1733
				return $this->setError(elFinder::ERROR_COPY_INTO_ITSELF, $errpath);
1734
			}
1735
			$method = $rmSrc ? 'move' : 'copy';
1736
			return ($path = $this->$method($source, $destination, $name)) ? $this->stat($path) : false;
1737
		}
1738
		
1739
		// copy/move from another volume
1740
		if (!$this->options['copyTo'] || !$volume->copyFromAllowed()) {
1741
			return $this->setError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_PERM_DENIED);
1742
		}
1743
		
1744
		if (($path = $this->copyFrom($volume, $src, $destination, $name)) == false) {
1745
			return false;
1746
		}
1747
		
1748
		if ($rmSrc) {
1749
			if (!$volume->rm($src)) {
1750
				return $this->setError(elFinder::ERROR_MOVE, $errpath, elFinder::ERROR_RM_SRC);
1751
			}
1752
		}
1753
		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 1744 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...
1754
	}
1755
	
1756
	/**
1757
	 * Return file contents
1758
	 *
1759
	 * @param  string  $hash  file hash
1760
	 * @return string|false
1761
	 * @author Dmitry (dio) Levashov
1762
	 **/
1763
	public function getContents($hash) {
1764
		$file = $this->file($hash);
1765
		
1766
		if (!$file) {
1767
			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...
1768
		}
1769
		
1770
		if ($file['mime'] == 'directory') {
1771
			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...
1772
		}
1773
		
1774
		if (!$file['read']) {
1775
			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...
1776
		}
1777
		
1778
		return $this->convEncOut($this->_getContents($this->convEncIn($this->decode($hash))));
1779
	}
1780
	
1781
	/**
1782
	 * Put content in text file and return file info.
1783
	 *
1784
	 * @param  string  $hash     file hash
1785
	 * @param  string  $content  new file content
1786
	 * @return array
1787
	 * @author Dmitry (dio) Levashov
1788
	 **/
1789
	public function putContents($hash, $content) {
1790
		if ($this->commandDisabled('edit')) {
1791
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1792
		}
1793
		
1794
		$path = $this->decode($hash);
1795
		
1796
		if (!($file = $this->file($hash))) {
1797
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1798
		}
1799
		
1800
		if (!$file['write']) {
1801
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1802
		}
1803
		
1804
		// check MIME
1805
		$name = $this->basenameCE($path);
1806
		$mime = '';
1807
		$mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name);
1808
		if ($this->mimeDetect !== 'internal') {
1809
			if ($tp = tmpfile()) {
1810
				fwrite($tp, $content);
1811
				$info = stream_get_meta_data($tp);
1812
				$filepath = $info['uri'];
1813
				$mime = $this->mimetype($filepath, $name);
1814
				fclose($tp);
1815
			}
1816
		}
1817 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...
1818
			return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME);
1819
		}
1820
		
1821
		$this->clearcache();
1822
		return $this->convEncOut($this->_filePutContents($this->convEncIn($path), $content)) ? $this->stat($path) : false;
1823
	}
1824
	
1825
	/**
1826
	 * Extract files from archive
1827
	 *
1828
	 * @param  string  $hash  archive hash
1829
	 * @return array|bool
1830
	 * @author Dmitry (dio) Levashov, 
1831
	 * @author Alexey Sukhotin
1832
	 **/
1833
	public function extract($hash, $makedir = null) {
1834
		if ($this->commandDisabled('extract')) {
1835
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1836
		}
1837
		
1838
		if (($file = $this->file($hash)) == false) {
1839
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1840
		}
1841
		
1842
		$archiver = isset($this->archivers['extract'][$file['mime']])
1843
			? $this->archivers['extract'][$file['mime']]
1844
			: false;
1845
			
1846
		if (!$archiver) {
1847
			return $this->setError(elFinder::ERROR_NOT_ARCHIVE);
1848
		}
1849
		
1850
		$path   = $this->decode($hash);
1851
		$parent = $this->stat($this->dirnameCE($path));
1852
1853
		if (!$file['read'] || !$parent['write']) {
1854
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1855
		}
1856
		$this->clearcache();
1857
		$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...
1858
		
1859
		if ($path = $this->convEncOut($this->_extract($this->convEncIn($path), $archiver))) {
1860
			if (is_array($path)) {
1861
				foreach ($path as $_k => $_p) {
1862
					$path[$_k] = $this->stat($_p);
1863
				}
1864
			} else {
1865
				$path = $this->stat($path);
1866
			}
1867
			return $path;
1868
		} else {
1869
			return false;
1870
		}
1871
	}
1872
1873
	/**
1874
	 * Add files to archive
1875
	 *
1876
	 * @return void
1877
	 **/
1878
	public function archive($hashes, $mime, $name = '') {
1879
		if ($this->commandDisabled('archive')) {
1880
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1881
		}
1882
1883
		$archiver = isset($this->archivers['create'][$mime])
1884
			? $this->archivers['create'][$mime]
1885
			: false;
1886
			
1887
		if (!$archiver) {
1888
			return $this->setError(elFinder::ERROR_ARCHIVE_TYPE);
1889
		}
1890
		
1891
		$files = array();
1892
		
1893
		foreach ($hashes as $hash) {
1894
			if (($file = $this->file($hash)) == false) {
1895
				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...
1896
			}
1897
			if (!$file['read']) {
1898
				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...
1899
			}
1900
			$path = $this->decode($hash);
1901
			if (!isset($dir)) {
1902
				$dir = $this->dirnameCE($path);
1903
				$stat = $this->stat($dir);
1904
				if (!$stat['write']) {
1905
					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...
1906
				}
1907
			}
1908
			
1909
			$files[] = $this->basenameCE($path);
1910
		}
1911
		
1912
		if ($name === '') {
1913
			$name = count($files) == 1 ? $files[0] : 'Archive';
1914
		} else {
1915
			$name = str_replace(array('/', '\\'), '_', preg_replace('/\.' . preg_quote($archiver['ext'], '/') . '$/i', '', $name));
1916
		}
1917
		$name .='.' . $archiver['ext'];
1918
		$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...
1919
		$this->clearcache();
1920
		return ($path = $this->convEncOut($this->_archive($this->convEncIn($dir), $this->convEncIn($files), $this->convEncIn($name), $archiver))) ? $this->stat($path) : false;
1921
	}
1922
	
1923
	/**
1924
	 * Resize image
1925
	 *
1926
	 * @param  string   $hash    image file
1927
	 * @param  int      $width   new width
1928
	 * @param  int      $height  new height
1929
	 * @param  int      $x       X start poistion for crop
1930
	 * @param  int      $y       Y start poistion for crop
1931
	 * @param  string   $mode    action how to mainpulate image
1932
	 * @return array|false
1933
	 * @author Dmitry (dio) Levashov
1934
	 * @author Alexey Sukhotin
1935
	 * @author nao-pon
1936
	 * @author Troex Nevelin
1937
	 **/
1938
	public function resize($hash, $width, $height, $x, $y, $mode = 'resize', $bg = '', $degree = 0) {
1939
		if ($this->commandDisabled('resize')) {
1940
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1941
		}
1942
		
1943
		if (($file = $this->file($hash)) == false) {
1944
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1945
		}
1946
		
1947
		if (!$file['write'] || !$file['read']) {
1948
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1949
		}
1950
		
1951
		$path = $this->decode($hash);
1952
		
1953
		$work_path = $this->getWorkFile($this->encoding? $this->convEncIn($path, true) : $path);
1954
1955
		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...
1956
			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...
1957
				@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...
1958
			}
1959
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1960
		}
1961
1962
		if ($this->imgLib != 'imagick') {
1963
			if (elFinder::isAnimationGif($work_path)) {
1964
				return $this->setError(elFinder::ERROR_UNSUPPORT_TYPE);
1965
			}
1966
		}
1967
1968
		switch($mode) {
1969
			
1970
			case 'propresize':
1971
				$result = $this->imgResize($work_path, $width, $height, true, true);
1972
				break;
1973
1974
			case 'crop':
1975
				$result = $this->imgCrop($work_path, $width, $height, $x, $y);
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...
1976
				break;
1977
1978
			case 'fitsquare':
1979
				$result = $this->imgSquareFit($work_path, $width, $height, 'center', 'middle', ($bg ? $bg : $this->options['tmbBgColor']));
1980
				break;
1981
1982
			case 'rotate':
1983
				$result = $this->imgRotate($work_path, $degree, ($bg ? $bg : $this->options['tmbBgColor']));
1984
				break;
1985
1986
			default:
1987
				$result = $this->imgResize($work_path, $width, $height, false, true);
1988
				break;
1989
		}
1990
		
1991
		$ret = false;
1992
		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...
1993
			$stat = $this->stat($path);
1994
			clearstatcache();
1995
			$fstat = stat($work_path);
1996
			$stat['size'] = $fstat['size'];
1997
			$stat['ts'] = $fstat['mtime'];
1998
			if ($imgsize = @getimagesize($work_path)) {
1999
				$stat['width'] = $imgsize[0];
2000
				$stat['height'] = $imgsize[1];
2001
				$stat['mime'] = $imgsize['mime'];
2002
			}
2003
			if ($path !== $work_path) {
2004
				if ($fp = @fopen($work_path, 'rb')) {
2005
					$ret = $this->saveCE($fp, $this->dirnameCE($path), $this->basenameCE($path), $stat);
2006
					@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...
2007
				}
2008
			} else {
2009
				$ret = true;
2010
			}
2011
			if ($ret) {
2012
				$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...
2013
				$this->clearcache();
2014
				$ret = $this->stat($path);
2015
				$ret['width'] = $stat['width'];
2016
				$ret['height'] = $stat['height'];
2017
			}
2018
		}
2019
		if ($path !== $work_path) {
2020
			is_file($work_path) && @unlink($work_path);
2021
		}
2022
		
2023
		return $ret;
2024
	}
2025
	
2026
	/**
2027
	 * Remove file/dir
2028
	 *
2029
	 * @param  string  $hash  file hash
2030
	 * @return bool
2031
	 * @author Dmitry (dio) Levashov
2032
	 **/
2033
	public function rm($hash) {
2034
		return $this->commandDisabled('rm')
2035
			? $this->setError(elFinder::ERROR_PERM_DENIED)
2036
			: $this->remove($this->decode($hash));
2037
	}
2038
	
2039
	/**
2040
	 * Search files
2041
	 *
2042
	 * @param  string  $q  search string
2043
	 * @param  array   $mimes
2044
	 * @return array
2045
	 * @author Dmitry (dio) Levashov
2046
	 **/
2047
	public function search($q, $mimes, $hash = null) {
2048
		$dir = null;
2049
		if ($hash) {
2050
			$dir = $this->decode($hash);
2051
			$stat = $this->stat($dir);
2052
			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...
2053
				$q = '';
2054
			}
2055
		}
2056
		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...
2057
			$mimes = array_intersect($mimes, $this->onlyMimes);
2058
			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...
2059
				$q = '';
2060
			}
2061
		}
2062
		return ($q === '' || $this->commandDisabled('search'))
2063
			? array()
2064
			: $this->doSearch(is_null($dir)? $this->root : $dir, $q, $mimes);
2065
	}
2066
	
2067
	/**
2068
	 * Return image dimensions
2069
	 *
2070
	 * @param  string  $hash  file hash
2071
	 * @return array
2072
	 * @author Dmitry (dio) Levashov
2073
	 **/
2074
	public function dimensions($hash) {
2075
		if (($file = $this->file($hash)) == false) {
2076
			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...
2077
		}
2078
		
2079
		return $this->convEncOut($this->_dimensions($this->convEncIn($this->decode($hash)), $file['mime']));
2080
	}
2081
	
2082
	/**
2083
	 * Return content URL (for netmout volume driver)
2084
	 * If file.url == 1 requests from JavaScript client with XHR
2085
	 * 
2086
	 * @param string $hash  file hash
2087
	 * @param array $options  options array
2088
	 * @return boolean|string
2089
	 * @author Naoki Sawada
2090
	 */
2091
	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...
2092 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...
2093
			return false;
2094
		}
2095
		return $file['url'];
2096
	}
2097
	
2098
	/**
2099
	 * Return temp path
2100
	 * 
2101
	 * @return string
2102
	 * @author Naoki Sawada
2103
	 */
2104
	public function getTempPath() {
2105
		if (@ $this->tmpPath) {
2106
			return $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...
2107
		} else if (@ $this->tmp) {
2108
			return $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...
2109
		} else if (function_exists('sys_get_temp_dir')) {
2110
			return sys_get_temp_dir();
2111
		} else if (@ $this->tmbPath) {
2112
			return $this->tmbPath;
2113
		} else {
2114
			return null;
2115
		}
2116
	}
2117
	
2118
	/**
2119
	 * (Make &) Get upload taget dirctory hash
2120
	 * 
2121
	 * @param string $baseTargetHash
2122
	 * @param string $path
2123
	 * @param array  $result
2124
	 * @return boolean|string
2125
	 * @author Naoki Sawada
2126
	 */
2127
	public function getUploadTaget($baseTargetHash, $path, & $result) {
2128
		$base = $this->decode($baseTargetHash);
2129
		$targetHash = $baseTargetHash;
2130
		$path = ltrim($path, $this->separator);
2131
		$dirs = explode($this->separator, $path);
2132
		array_pop($dirs);
2133
		foreach($dirs as $dir) {
2134
			$targetPath = $this->joinPathCE($base, $dir);
2135
			if (! $_realpath = $this->realpath($this->encode($targetPath))) {
2136
				if ($stat = $this->mkdir($targetHash, $dir)) {
2137
					$result['added'][] = $stat;
2138
					$targetHash = $stat['hash'];
2139
					$base = $this->decode($targetHash);
2140
				} else {
2141
					return false;
2142
				}
2143
			} else {
2144
				$targetHash = $this->encode($_realpath);
2145
				if ($this->dir($targetHash)) {
2146
					$base = $this->decode($targetHash);
2147
				} else {
2148
					return false;
2149
				}
2150
			}
2151
		}
2152
		return $targetHash;
2153
	}
2154
	
2155
	/**
2156
	 * Return this uploadMaxSize value
2157
	 * 
2158
	 * @return integer
2159
	 * @author Naoki Sawada
2160
	 */
2161
	public function getUploadMaxSize() {
2162
		return $this->uploadMaxSize;
2163
	}
2164
	
2165
	/**
2166
	 * Save error message
2167
	 *
2168
	 * @param  array  error 
2169
	 * @return false
2170
	 * @author Dmitry(dio) Levashov
2171
	 **/
2172
	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...
2173
		
2174
		$this->error = array();
2175
		
2176
		foreach (func_get_args() as $err) {
2177
			if (is_array($err)) {
2178
				$this->error = array_merge($this->error, $err);
2179
			} else {
2180
				$this->error[] = $err;
2181
			}
2182
		}
2183
		
2184
		// $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...
2185
		return false;
2186
	}
2187
	
2188
	/*********************************************************************/
2189
	/*                               FS API                              */
2190
	/*********************************************************************/
2191
	
2192
	/***************** server encoding support *******************/
2193
	
2194
	/**
2195
	 * Return parent directory path (with convert encording)
2196
	 *
2197
	 * @param  string  $path  file path
2198
	 * @return string
2199
	 * @author Naoki Sawada
2200
	 **/
2201
	protected function dirnameCE($path) {
2202
		return (!$this->encoding)? $this->_dirname($path) :	$this->convEncOut($this->_dirname($this->convEncIn($path)));
2203
	}
2204
	
2205
	/**
2206
	 * Return file name (with convert encording)
2207
	 *
2208
	 * @param  string  $path  file path
2209
	 * @return string
2210
	 * @author Naoki Sawada
2211
	 **/
2212
	protected function basenameCE($path) {
2213
		return (!$this->encoding)? $this->_basename($path) : $this->convEncOut($this->_basename($this->convEncIn($path)));
2214
	}
2215
	
2216
	/**
2217
	 * Join dir name and file name and return full path. (with convert encording)
2218
	 * Some drivers (db) use int as path - so we give to concat path to driver itself
2219
	 *
2220
	 * @param  string  $dir   dir path
2221
	 * @param  string  $name  file name
2222
	 * @return string
2223
	 * @author Naoki Sawada
2224
	 **/
2225
	protected function joinPathCE($dir, $name) {
2226
		return (!$this->encoding)? $this->_joinPath($dir, $name) : $this->convEncOut($this->_joinPath($this->convEncIn($dir), $this->convEncIn($name)));
2227
	}
2228
	
2229
	/**
2230
	 * Return normalized path (with convert encording)
2231
	 *
2232
	 * @param  string  $path  file path
2233
	 * @return string
2234
	 * @author Naoki Sawada
2235
	 **/
2236
	protected function normpathCE($path) {
2237
		return (!$this->encoding)? $this->_normpath($path) : $this->convEncOut($this->_normpath($this->convEncIn($path)));
2238
	}
2239
	
2240
	/**
2241
	 * Return file path related to root dir (with convert encording)
2242
	 *
2243
	 * @param  string  $path  file path
2244
	 * @return string
2245
	 * @author Naoki Sawada
2246
	 **/
2247
	protected function relpathCE($path) {
2248
		return (!$this->encoding)? $this->_relpath($path) : $this->convEncOut($this->_relpath($this->convEncIn($path)));
2249
	}
2250
	
2251
	/**
2252
	 * Convert path related to root dir into real path (with convert encording)
2253
	 *
2254
	 * @param  string  $path  rel file path
2255
	 * @return string
2256
	 * @author Naoki Sawada
2257
	 **/
2258
	protected function abspathCE($path) {
2259
		return (!$this->encoding)? $this->_abspath($path): $this->convEncOut($this->_abspath($this->convEncIn($path)));
2260
	}
2261
	
2262
	/**
2263
	 * Return true if $path is children of $parent (with convert encording)
2264
	 *
2265
	 * @param  string  $path    path to check
2266
	 * @param  string  $parent  parent path
2267
	 * @return bool
2268
	 * @author Naoki Sawada
2269
	 **/
2270
	protected function inpathCE($path, $parent) {
2271
		return (!$this->encoding)? $this->_inpath($path, $parent) : $this->convEncOut($this->_inpath($this->convEncIn($path), $this->convEncIn($parent)));
2272
	}
2273
	
2274
	/**
2275
	 * Open file and return file pointer (with convert encording)
2276
	 *
2277
	 * @param  string  $path  file path
2278
	 * @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...
2279
	 * @return resource|false
2280
	 * @author Naoki Sawada
2281
	 **/
2282
	protected function fopenCE($path, $mode='rb') {
2283
		return (!$this->encoding)? $this->_fopen($path, $mode) : $this->convEncOut($this->_fopen($this->convEncIn($path), $mode));
2284
	}
2285
	
2286
	/**
2287
	 * Close opened file (with convert encording)
2288
	 * 
2289
	 * @param  resource  $fp    file pointer
2290
	 * @param  string    $path  file path
2291
	 * @return bool
2292
	 * @author Naoki Sawada
2293
	 **/
2294
	protected function fcloseCE($fp, $path='') {
2295
		return (!$this->encoding)? $this->_fclose($fp, $path) : $this->convEncOut($this->_fclose($fp, $this->convEncIn($path)));
2296
	}
2297
	
2298
	/**
2299
	 * Create new file and write into it from file pointer. (with convert encording)
2300
	 * Return new file path or false on error.
2301
	 *
2302
	 * @param  resource  $fp   file pointer
2303
	 * @param  string    $dir  target dir path
2304
	 * @param  string    $name file name
2305
	 * @param  array     $stat file stat (required by some virtual fs)
2306
	 * @return bool|string
2307
	 * @author Naoki Sawada
2308
	 **/
2309
	protected function saveCE($fp, $dir, $name, $stat) {
2310
		return (!$this->encoding)? $this->_save($fp, $dir, $name, $stat) : $this->convEncOut($this->_save($fp, $this->convEncIn($dir), $this->convEncIn($name), $this->convEncIn($stat)));
2311
	}
2312
	
2313
	/**
2314
	 * Return true if path is dir and has at least one childs directory (with convert encording)
2315
	 *
2316
	 * @param  string  $path  dir path
2317
	 * @return bool
2318
	 * @author Naoki Sawada
2319
	 **/
2320
	protected function subdirsCE($path) {
2321
		if (!isset($this->subdirsCache[$path])) {
2322
			$this->subdirsCache[$path] = (!$this->encoding)? $this->_subdirs($path) : $this->convEncOut($this->_subdirs($this->convEncIn($path)));
2323
		}
2324
		return $this->subdirsCache[$path];
2325
	}
2326
	
2327
	/**
2328
	 * Return files list in directory (with convert encording)
2329
	 *
2330
	 * @param  string  $path  dir path
2331
	 * @return array
2332
	 * @author Naoki Sawada
2333
	 **/
2334
	protected function scandirCE($path) {
2335
		return (!$this->encoding)? $this->_scandir($path) : $this->convEncOut($this->_scandir($this->convEncIn($path)));
2336
	}
2337
	
2338
	/**
2339
	 * Create symlink (with convert encording)
2340
	 *
2341
	 * @param  string  $source     file to link to
2342
	 * @param  string  $targetDir  folder to create link in
2343
	 * @param  string  $name       symlink name
2344
	 * @return bool
2345
	 * @author Naoki Sawada
2346
	 **/
2347
	protected function symlinkCE($source, $targetDir, $name) {
2348
		return (!$this->encoding)? $this->_symlink($source, $targetDir, $name) : $this->convEncOut($this->_symlink($this->convEncIn($source), $this->convEncIn($targetDir), $this->convEncIn($name)));
2349
	}
2350
	
2351
	/***************** paths *******************/
2352
	
2353
	/**
2354
	 * Encode path into hash
2355
	 *
2356
	 * @param  string  file path
2357
	 * @return string
2358
	 * @author Dmitry (dio) Levashov
2359
	 * @author Troex Nevelin
2360
	 **/
2361
	protected function encode($path) {
2362
		if ($path !== '') {
2363
2364
			// cut ROOT from $path for security reason, even if hacker decodes the path he will not know the root
2365
			$p = $this->relpathCE($path);
2366
			// if reqesting root dir $path will be empty, then assign '/' as we cannot leave it blank for crypt
2367
			if ($p === '')	{
2368
				$p = DIRECTORY_SEPARATOR;
2369
			}
2370
2371
			// TODO crypt path and return hash
2372
			$hash = $this->crypt($p);
2373
			// hash is used as id in HTML that means it must contain vaild chars
2374
			// make base64 html safe and append prefix in begining
2375
			$hash = strtr(base64_encode($hash), '+/=', '-_.');
2376
			// remove dots '.' at the end, before it was '=' in base64
2377
			$hash = rtrim($hash, '.'); 
2378
			// append volume id to make hash unique
2379
			return $this->id.$hash;
2380
		}
2381
	}
2382
	
2383
	/**
2384
	 * Decode path from hash
2385
	 *
2386
	 * @param  string  file hash
2387
	 * @return string
2388
	 * @author Dmitry (dio) Levashov
2389
	 * @author Troex Nevelin
2390
	 **/
2391
	protected function decode($hash) {
2392
		if (strpos($hash, $this->id) === 0) {
2393
			// cut volume id after it was prepended in encode
2394
			$h = substr($hash, strlen($this->id));
2395
			// replace HTML safe base64 to normal
2396
			$h = base64_decode(strtr($h, '-_.', '+/='));
2397
			// TODO uncrypt hash and return path
2398
			$path = $this->uncrypt($h); 
2399
			// append ROOT to path after it was cut in encode
2400
			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...
2401
		}
2402
	}
2403
	
2404
	/**
2405
	 * Return crypted path 
2406
	 * Not implemented
2407
	 *
2408
	 * @param  string  path
2409
	 * @return mixed
2410
	 * @author Dmitry (dio) Levashov
2411
	 **/
2412
	protected function crypt($path) {
2413
		return $path;
2414
	}
2415
	
2416
	/**
2417
	 * Return uncrypted path 
2418
	 * Not implemented
2419
	 *
2420
	 * @param  mixed  hash
2421
	 * @return mixed
2422
	 * @author Dmitry (dio) Levashov
2423
	 **/
2424
	protected function uncrypt($hash) {
2425
		return $hash;
2426
	}
2427
	
2428
	/**
2429
	 * Validate file name based on $this->options['acceptedName'] regexp or function
2430
	 *
2431
	 * @param  string  $name  file name
2432
	 * @return bool
2433
	 * @author Dmitry (dio) Levashov
2434
	 **/
2435
	protected function nameAccepted($name) {
2436
		if ($this->nameValidator) {
2437
			if (is_callable($this->nameValidator)) {
2438
				$res = call_user_func($this->nameValidator, $name);
2439
				return $res;
2440
			}
2441
			if (preg_match($this->nameValidator, '') !== false) {
2442
				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...
2443
			}
2444
		}
2445
		return true;
2446
	}
2447
	
2448
	/**
2449
	 * Return new unique name based on file name and suffix
2450
	 *
2451
	 * @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...
2452
	 * @param  string  $suffix  suffix append to name
2453
	 * @return string
2454
	 * @author Dmitry (dio) Levashov
2455
	 **/
2456
	public function uniqueName($dir, $name, $suffix = ' copy', $checkNum = true, $start = 1) {
2457
		$ext  = '';
2458
2459
		if (preg_match('/\.((tar\.(gz|bz|bz2|z|lzo))|cpio\.gz|ps\.gz|xcf\.(gz|bz2)|[a-z0-9]{1,4})$/i', $name, $m)) {
2460
			$ext  = '.'.$m[1];
2461
			$name = substr($name, 0,  strlen($name)-strlen($m[0]));
2462
		} 
2463
		
2464
		if ($checkNum && preg_match('/('.preg_quote($suffix, '/').')(\d*)$/i', $name, $m)) {
2465
			$i    = (int)$m[2];
2466
			$name = substr($name, 0, strlen($name)-strlen($m[2]));
2467
		} else {
2468
			$i     = $start;
2469
			$name .= $suffix;
2470
		}
2471
		$max = $i+100000;
2472
2473
		while ($i <= $max) {
2474
			$n = $name.($i > 0 ? $i : '').$ext;
2475
2476
			if (!$this->stat($this->joinPathCE($dir, $n))) {
2477
				$this->clearcache();
2478
				return $n;
2479
			}
2480
			$i++;
2481
		}
2482
		return $name.md5($dir).$ext;
2483
	}
2484
	
2485
	/**
2486
	 * Converts character encoding from UTF-8 to server's one
2487
	 * 
2488
	 * @param  mixed  $var           target string or array var
2489
	 * @param  bool   $restoreLocale do retore global locale, default is false
2490
	 * @param  string $unknown       replaces character for unknown
2491
	 * @return mixed
2492
	 * @author Naoki Sawada
2493
	 */
2494
	public function convEncIn($var = null, $restoreLocale = false, $unknown = '_') {
2495
		return (!$this->encoding)? $var : $this->convEnc($var, 'UTF-8', $this->encoding, $this->options['locale'], $restoreLocale, $unknown);
2496
	}
2497
	
2498
	/**
2499
	 * Converts character encoding from server's one to UTF-8
2500
	 * 
2501
	 * @param  mixed  $var           target string or array var
2502
	 * @param  bool   $restoreLocale do retore global locale, default is true
2503
	 * @param  string $unknown       replaces character for unknown
2504
	 * @return mixed
2505
	 * @author Naoki Sawada
2506
	 */
2507
	public function convEncOut($var = null, $restoreLocale = true, $unknown = '_') {
2508
		return (!$this->encoding)? $var : $this->convEnc($var, $this->encoding, 'UTF-8', $this->options['locale'], $restoreLocale, $unknown);
2509
	}
2510
	
2511
	/**
2512
	 * Converts character encoding (base function)
2513
	 * 
2514
	 * @param  mixed  $var     target string or array var
2515
	 * @param  string $from    from character encoding
2516
	 * @param  string $to      to character encoding
2517
	 * @param  string $locale  local locale
2518
	 * @param  string $unknown replaces character for unknown
2519
	 * @return mixed
2520
	 */
2521
	protected function convEnc($var, $from, $to, $locale, $restoreLocale, $unknown = '_') {
2522
		if (strtoupper($from) !== strtoupper($to)) {
2523
			if ($locale) {
2524
				@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...
2525
			}
2526
			if (is_array($var)) {
2527
				$_ret = array();
2528
				foreach($var as $_k => $_v) {
2529
					$_ret[$_k] = $this->convEnc($_v, $from, $to, '', false, $unknown = '_');
2530
				}
2531
				$var = $_ret;
2532
			} else {
2533
				$_var = false;
2534
				if (is_string($var)) {
2535
					$_var = $var;
2536
					if (false !== ($_var = @iconv($from, $to.'//TRANSLIT', $_var))) {
2537
						$_var = str_replace('?', $unknown, $_var);
2538
					}
2539
				}
2540
				if  ($_var !== false) {
2541
					$var = $_var;
2542
				}
2543
			}
2544
			if ($restoreLocale) {
2545
				setlocale(LC_ALL, elFinder::$locale);
2546
			}
2547
		}
2548
		return $var;
2549
	}
2550
	
2551
	/*********************** util mainly for inheritance class *********************/
2552
	
2553
	/**
2554
	 * Get temporary filename. Tempfile will be removed when after script execution finishes or exit() is called.
2555
	 * When needing the unique file to a path, give $path to parameter.
2556
	 * 
2557
	 * @param  string       $path for get unique file to a path
2558
	 * @return string|false
2559
	 * @author Naoki Sawada
2560
	 */
2561
	protected function getTempFile($path = '') {
2562
		static $cache = array();
2563
		static $rmfunc;
2564
		
2565
		$key = '';
2566
		if ($path !== '') {
2567
			$key = $this->id . '#' . $path;
2568
			if (isset($cache[$key])) {
2569
				return $cache[$key];
2570
			}
2571
		}
2572
		
2573
		if ($tmpdir = $this->getTempPath()) {
2574
			if (!$rmfunc) {
2575
				$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...
2576
			}
2577
			$name = tempnam($tmpdir, 'ELF');
2578
			if ($key) {
2579
				$cache[$key] = $name;
2580
			}
2581
			register_shutdown_function($rmfunc, $name);
2582
			return $name;
2583
		}
2584
		
2585
		return false;
2586
	}
2587
	
2588
	/**
2589
	 * File path of local server side work file path
2590
	 * 
2591
	 * @param  string $path path need convert encoding to server encoding
2592
	 * @return string
2593
	 * @author Naoki Sawada
2594
	 */
2595
	protected function getWorkFile($path) {
2596
		if ($work = $this->getTempFile()) {
2597
			if ($wfp = fopen($work, 'wb')) {
2598
				if ($fp = $this->_fopen($path)) {
2599
					while(!feof($fp)) {
2600
						fwrite($wfp, fread($fp, 8192));
2601
					}
2602
					$this->_fclose($fp, $path);
2603
					fclose($wfp);
2604
					return $work;
2605
				}
2606
			}
2607
		}
2608
		return false;
2609
	}
2610
	
2611
	/**
2612
	 * Get image size array with `dimensions`
2613
	 *
2614
	 * @param string $path path need convert encoding to server encoding
2615
	 * @param string $mime file mime type
2616
	 * @return array|false
2617
	 */
2618
	public function getImageSize($path, $mime = '') {
2619
		$size = false;
2620
		if ($mime === '' || strtolower(substr($mime, 0, 5)) === 'image') {
2621
			if ($work = $this->getWorkFile($path)) {
2622
				if ($size = @getimagesize($work)) {
2623
					$size['dimensions'] = $size[0].'x'.$size[1];
2624
				}
2625
			}
2626
			is_file($work) && @unlink($work);
2627
		}
2628
		return $size;
2629
	}
2630
	
2631
	/**
2632
	 * Delete dirctory trees
2633
	 *
2634
	 * @param string $localpath path need convert encoding to server encoding
2635
	 * @return boolean
2636
	 * @author Naoki Sawada
2637
	 */
2638
	protected function delTree($localpath) {
2639
		foreach ($this->_scandir($localpath) as $p) {
2640
			@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...
2641
			$stat = $this->stat($this->convEncOut($p));
2642
			$this->convEncIn();
2643
			($stat['mime'] === 'directory')? $this->delTree($p) : $this->_unlink($p);
2644
		}
2645
		return $this->_rmdir($localpath);
2646
	}
2647
	
2648
	/*********************** file stat *********************/
2649
	
2650
	/**
2651
	 * Check file attribute
2652
	 *
2653
	 * @param  string  $path  file path
2654
	 * @param  string  $name  attribute name (read|write|locked|hidden)
2655
	 * @param  bool    $val   attribute value returned by file system
2656
	 * @param  bool    $isDir path is directory (true: directory, false: file)
2657
	 * @return bool
2658
	 * @author Dmitry (dio) Levashov
2659
	 **/
2660
	protected function attr($path, $name, $val=null, $isDir=null) {
2661
		if (!isset($this->defaults[$name])) {
2662
			return false;
2663
		}
2664
		
2665
		
2666
		$perm = null;
2667
		
2668 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...
2669
			$perm = call_user_func($this->access, $name, $path, $this->options['accessControlData'], $this, $isDir);
2670
2671
			if ($perm !== null) {
2672
				return !!$perm;
2673
			}
2674
		}
2675
		
2676
		if ($this->separator != '/') {
2677
			$path = str_replace($this->separator, '/', $this->relpathCE($path));
2678
		} else {
2679
			$path = $this->relpathCE($path);
2680
		}
2681
2682
		$path = '/'.$path;
2683
2684 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...
2685
			$attrs = $this->attributes[$i];
2686
			
2687
			if (isset($attrs[$name]) && isset($attrs['pattern']) && preg_match($attrs['pattern'], $path)) {
2688
				$perm = $attrs[$name];
2689
			} 
2690
		}
2691
		
2692
		return $perm === null ? (is_null($val)? $this->defaults[$name] : $val) : !!$perm;
2693
	}
2694
	
2695
	/**
2696
	 * Return true if file with given name can be created in given folder.
2697
	 *
2698
	 * @param string $dir  parent dir path
2699
	 * @param string $name new file name
2700
	 * @return bool
2701
	 * @author Dmitry (dio) Levashov
2702
	 **/
2703
	protected function allowCreate($dir, $name, $isDir = null) {
2704
		$path = $this->joinPathCE($dir, $name);
2705
		$perm = null;
2706
		
2707 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...
2708
			$perm = call_user_func($this->access, 'write', $path, $this->options['accessControlData'], $this, $isDir);			
2709
			if ($perm !== null) {
2710
				return !!$perm;
2711
			}
2712
		}
2713
		
2714
		$testPath = $this->separator.$this->relpathCE($path);
2715
		
2716 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...
2717
			$attrs = $this->attributes[$i];
2718
			
2719
			if (isset($attrs['write']) && isset($attrs['pattern']) && preg_match($attrs['pattern'], $testPath)) {
2720
				$perm = $attrs['write'];
2721
			} 
2722
		}
2723
		
2724
		return $perm === null ? true : $perm;
2725
	}
2726
	
2727
	/**
2728
	 * Return true if file MIME type can save with check uploadOrder config.
2729
	 * 
2730
	 * @param string $mime
2731
	 * @return boolean
2732
	 */
2733
	protected function allowPutMime($mime) {
2734
		// logic based on http://httpd.apache.org/docs/2.2/mod/mod_authz_host.html#order
2735
		$allow  = $this->mimeAccepted($mime, $this->uploadAllow, null);
2736
		$deny   = $this->mimeAccepted($mime, $this->uploadDeny,  null);
2737
		$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...
2738
		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...
2739
			$res = false; // default is deny
2740
			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...
2741
				$res = true;
2742
			}// else (both match | no match | match only deny) { deny }
2743
		} else { // array('deny', 'allow'), default is to 'allow' - this is the default rule
2744
			$res = true; // default is allow
2745
			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...
2746
				$res = false;
2747
			} // else (both match | no match | match only allow) { allow }
2748
		}
2749
		return $res;
2750
	}
2751
	
2752
	/**
2753
	 * Return fileinfo 
2754
	 *
2755
	 * @param  string  $path  file cache
2756
	 * @return array
2757
	 * @author Dmitry (dio) Levashov
2758
	 **/
2759
	protected function stat($path) {
2760
		if ($path === false || is_null($path)) {
2761
			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...
2762
		}
2763
		$is_root = ($path === $this->root);
2764
		if ($is_root) {
2765
			$rootKey = md5($path);
2766
			if (!isset($this->sessionCache['rootstat'])) {
2767
				$this->sessionCache['rootstat'] = array();
2768
			}
2769
			if (empty($this->ARGS['reload']) || empty($this->ARGS['target']) || strpos($this->ARGS['target'], $this->id) !== 0) {
2770
				// need $path as key for netmount/netunmount
2771
				if (isset($this->sessionCache['rootstat'][$rootKey])) {
2772
					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...
2773
						return $ret;
2774
					}
2775
				}
2776
			}
2777
		}
2778
		$ret = isset($this->cache[$path])
2779
			? $this->cache[$path]
2780
			: $this->updateCache($path, $this->convEncOut($this->_stat($this->convEncIn($path))));
2781
		if ($is_root) {
2782
			$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...
2783
		}
2784
		return $ret;
2785
	}
2786
	
2787
	/**
2788
	 * Put file stat in cache and return it
2789
	 *
2790
	 * @param  string  $path   file path
2791
	 * @param  array   $stat   file stat
2792
	 * @return array
2793
	 * @author Dmitry (dio) Levashov
2794
	 **/
2795
	protected function updateCache($path, $stat) {
2796
		if (empty($stat) || !is_array($stat)) {
2797
			return $this->cache[$path] = array();
2798
		}
2799
2800
		$stat['hash'] = $this->encode($path);
2801
2802
		$root = $path == $this->root;
2803
		$parent = '';
2804
		
2805
		if ($root) {
2806
			if ($this->rootName) {
2807
				$stat['name'] = $this->rootName;
2808
			}
2809
			if (! empty($this->options['icon'])) {
2810
				$stat['icon'] = $this->options['icon'];
2811
			}
2812
			if (! empty($this->options['rootCssClass'])) {
2813
				$stat['csscls'] = $this->options['rootCssClass'];
2814
			}
2815
		} else {
2816
			if (!isset($stat['name']) || $stat['name'] === '') {
2817
				$stat['name'] = $this->basenameCE($path);
2818
			}
2819
			if (empty($stat['phash'])) {
2820
				$parent = $this->dirnameCE($path);
2821
				$stat['phash'] = $this->encode($parent);
2822
			}
2823
		}
2824
		
2825
		// fix name if required
2826
		if ($this->options['utf8fix'] && $this->options['utf8patterns'] && $this->options['utf8replace']) {
2827
			$stat['name'] = json_decode(str_replace($this->options['utf8patterns'], $this->options['utf8replace'], json_encode($stat['name'])));
2828
		}
2829
		
2830
		
2831
		if (empty($stat['mime'])) {
2832
			$stat['mime'] = $this->mimetype($stat['name']);
2833
		}
2834
		
2835
		// @todo move dateformat to client
2836
		// $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...
2837
		// 	? $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...
2838
		// 	: 'unknown';
2839
			
2840
		if (!isset($stat['size'])) {
2841
			$stat['size'] = 'unknown';
2842
		}	
2843
2844
		if ($isDir = ($stat['mime'] === 'directory')) {
2845
			$stat['volumeid'] = $this->id;
2846
		}
2847
		
2848
		$stat['read']  = intval($this->attr($path, 'read', isset($stat['read']) ? !!$stat['read'] : null, $isDir));
2849
		$stat['write'] = intval($this->attr($path, 'write', isset($stat['write']) ? !!$stat['write'] : null, $isDir));
2850
		if ($root) {
2851
			$stat['locked'] = 1;
2852
		} else {
2853
			// lock when parent directory is not writable
2854
			if (!isset($stat['locked'])) {
2855
				$parent = $this->dirnameCE($path);
2856
				$pstat = isset($this->cache[$parent])? $this->cache[$parent] : array();
2857
				if (isset($pstat['write']) && !$pstat['write']) {
2858
					$stat['locked'] = true;
2859
				}
2860
			}
2861
			if ($this->attr($path, 'locked', isset($stat['locked']) ? !!$stat['locked'] : null, $isDir)) {
2862
				$stat['locked'] = 1;
2863
			} else {
2864
				unset($stat['locked']);
2865
			}
2866
		}
2867
2868
		if ($root) {
2869
			unset($stat['hidden']);
2870
		} elseif ($this->attr($path, 'hidden', isset($stat['hidden']) ? !!$stat['hidden'] : null, $isDir) 
2871
		|| !$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...
2872
			$stat['hidden'] = 1;
2873
		} else {
2874
			unset($stat['hidden']);
2875
		}
2876
		
2877
		if ($stat['read'] && empty($stat['hidden'])) {
2878
			
2879
			if ($isDir) {
2880
				// caching parent's subdirs
2881
				if ($parent) {
2882
					$this->subdirsCache[$parent] = true;
2883
				}
2884
				// for dir - check for subdirs
2885
				if ($this->options['checkSubfolders']) {
2886
					if (isset($stat['dirs'])) {
2887
						if ($stat['dirs']) {
2888
							$stat['dirs'] = 1;
2889
						} else {
2890
							unset($stat['dirs']);
2891
						}
2892
					} elseif (!empty($stat['alias']) && !empty($stat['target'])) {
2893
						$stat['dirs'] = isset($this->cache[$stat['target']])
2894
							? intval(isset($this->cache[$stat['target']]['dirs']))
2895
							: $this->subdirsCE($stat['target']);
2896
						
2897
					} elseif ($this->subdirsCE($path)) {
2898
						$stat['dirs'] = 1;
2899
					}
2900
				} else {
2901
					$stat['dirs'] = 1;
2902
				}
2903
			} else {
2904
				// for files - check for thumbnails
2905
				$p = isset($stat['target']) ? $stat['target'] : $path;
2906
				if ($this->tmbURL && !isset($stat['tmb']) && $this->canCreateTmb($p, $stat)) {
2907
					$tmb = $this->gettmb($p, $stat);
2908
					$stat['tmb'] = $tmb ? $tmb : 1;
2909
				}
2910
				
2911
			}
2912
			if (!isset($stat['url']) && $this->URL && $this->encoding) {
2913
				$_path = str_replace($this->separator, '/', substr($path, strlen($this->root) + 1));
2914
				$stat['url'] = rtrim($this->URL, '/') . '/' . str_replace('%2F', '/', rawurlencode((substr(PHP_OS, 0, 3) === 'WIN')? $_path : $this->convEncIn($_path, true)));
2915
			}
2916
		} else {
2917
			if ($isDir) {
2918
				unset($stat['dirs']);
2919
			}
2920
		}
2921
		
2922
		if (!empty($stat['alias']) && !empty($stat['target'])) {
2923
			$stat['thash'] = $this->encode($stat['target']);
2924
			//$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...
2925
			unset($stat['target']);
2926
		}
2927
		
2928
		if (isset($this->options['netkey']) && $path === $this->root) {
2929
			$stat['netkey'] = $this->options['netkey'];
2930
		}
2931
		
2932
		return $this->cache[$path] = $stat;
2933
	}
2934
	
2935
	/**
2936
	 * Get stat for folder content and put in cache
2937
	 *
2938
	 * @param  string  $path
2939
	 * @return void
2940
	 * @author Dmitry (dio) Levashov
2941
	 **/
2942
	protected function cacheDir($path) {
2943
		$this->dirsCache[$path] = array();
2944
		$this->subdirsCache[$path] = false;
2945
2946
		foreach ($this->scandirCE($path) as $p) {
2947 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...
2948
				if ($stat['mime'] === 'directory') {
2949
					$this->subdirsCache[$path] = true;
2950
				}
2951
				$this->dirsCache[$path][] = $p;
2952
			}
2953
		}
2954
	}
2955
	
2956
	/**
2957
	 * Clean cache
2958
	 *
2959
	 * @return void
2960
	 * @author Dmitry (dio) Levashov
2961
	 **/
2962
	protected function clearcache() {
2963
		$this->cache = $this->dirsCache = array();
2964
		unset($this->sessionCache['rootstat'][md5($this->root)]);
2965
	}
2966
	
2967
	/**
2968
	 * Return file mimetype
2969
	 *
2970
	 * @param  string  $path  file path
2971
	 * @return string
2972
	 * @author Dmitry (dio) Levashov
2973
	 **/
2974
	protected function mimetype($path, $name = '') {
2975
		$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...
2976
		
2977
		if ($name === '') {
2978
			$name = $path;
2979
		}
2980
		$ext = (false === $pos = strrpos($name, '.')) ? '' : substr($name, $pos + 1);
2981
		if ($this->mimeDetect == 'finfo') {
2982
			if ($type = @finfo_file($this->finfo, $path)) {
2983
				if ($ext && preg_match('~^application/(?:octet-stream|(?:x-)?zip)~', $type)) {
2984
					if (isset(elFinderVolumeDriver::$mimetypes[$ext])) $type = elFinderVolumeDriver::$mimetypes[$ext];
2985
				} else if ($ext === 'js' && preg_match('~^text/~', $type)) {
2986
					$type = 'text/javascript';
2987
				}
2988
			} else {
2989
				$type = 'unknown';
2990
			}
2991
		} elseif ($this->mimeDetect == 'mime_content_type') {
2992
			$type = mime_content_type($path);
2993
		} else {
2994
			$type = elFinderVolumeDriver::mimetypeInternalDetect($path);
2995
		}
2996
		
2997
		$type = explode(';', $type);
2998
		$type = trim($type[0]);
2999
3000 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...
3001
			// finfo return this mime for empty files
3002
			$type = 'text/plain';
3003
		} elseif ($type == 'application/x-zip') {
3004
			// http://elrte.org/redmine/issues/163
3005
			$type = 'application/zip';
3006
		}
3007
		
3008
		// mime type normalization
3009
		$_checkKey = strtolower($ext.':'.$type);
3010
		if (isset($this->options['mimeMap'][$_checkKey])) {
3011
			$type = $this->options['mimeMap'][$_checkKey];
3012
		}
3013
		
3014
		return $type == 'unknown' && $this->mimeDetect != 'internal'
3015
			? elFinderVolumeDriver::mimetypeInternalDetect($path)
3016
			: $type;
3017
		
3018
	}
3019
	
3020
	/**
3021
	 * Detect file mimetype using "internal" method
3022
	 *
3023
	 * @param  string  $path  file path
3024
	 * @return string
3025
	 * @author Dmitry (dio) Levashov
3026
	 **/
3027
	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...
3028
		$pinfo = pathinfo($path); 
3029
		$ext   = isset($pinfo['extension']) ? strtolower($pinfo['extension']) : '';
3030
		return isset(elFinderVolumeDriver::$mimetypes[$ext]) ? elFinderVolumeDriver::$mimetypes[$ext] : 'unknown';
3031
		
3032
	}
3033
	
3034
	/**
3035
	 * Return file/total directory size
3036
	 *
3037
	 * @param  string  $path  file path
3038
	 * @return int
3039
	 * @author Dmitry (dio) Levashov
3040
	 **/
3041
	protected function countSize($path) {
3042
		$stat = $this->stat($path);
3043
3044
		if (empty($stat) || !$stat['read'] || !empty($stat['hidden'])) {
3045
			return 'unknown';
3046
		}
3047
		
3048
		if ($stat['mime'] != 'directory') {
3049
			return $stat['size'];
3050
		}
3051
		
3052
		$subdirs = $this->options['checkSubfolders'];
3053
		$this->options['checkSubfolders'] = true;
3054
		$result = 0;
3055
		foreach ($this->getScandir($path) as $stat) {
3056
			$size = $stat['mime'] == 'directory' && $stat['read'] 
3057
				? $this->countSize($this->joinPathCE($path, $stat['name'])) 
3058
				: (isset($stat['size']) ? intval($stat['size']) : 0);
3059
			if ($size > 0) {
3060
				$result += $size;
3061
			}
3062
		}
3063
		$this->options['checkSubfolders'] = $subdirs;
3064
		return $result;
3065
	}
3066
	
3067
	/**
3068
	 * Return true if all mimes is directory or files
3069
	 *
3070
	 * @param  string  $mime1  mimetype
3071
	 * @param  string  $mime2  mimetype
3072
	 * @return bool
3073
	 * @author Dmitry (dio) Levashov
3074
	 **/
3075
	protected function isSameType($mime1, $mime2) {
3076
		return ($mime1 == 'directory' && $mime1 == $mime2) || ($mime1 != 'directory' && $mime2 != 'directory');
3077
	}
3078
	
3079
	/**
3080
	 * If file has required attr == $val - return file path,
3081
	 * If dir has child with has required attr == $val - return child path
3082
	 *
3083
	 * @param  string   $path  file path
3084
	 * @param  string   $attr  attribute name
3085
	 * @param  bool     $val   attribute value
3086
	 * @return string|false
3087
	 * @author Dmitry (dio) Levashov
3088
	 **/
3089
	protected function closestByAttr($path, $attr, $val) {
3090
		$stat = $this->stat($path);
3091
		
3092
		if (empty($stat)) {
3093
			return false;
3094
		}
3095
		
3096
		$v = isset($stat[$attr]) ? $stat[$attr] : false;
3097
		
3098
		if ($v == $val) {
3099
			return $path;
3100
		}
3101
3102
		return $stat['mime'] == 'directory'
3103
			? $this->childsByAttr($path, $attr, $val) 
3104
			: false;
3105
	}
3106
	
3107
	/**
3108
	 * Return first found children with required attr == $val
3109
	 *
3110
	 * @param  string   $path  file path
3111
	 * @param  string   $attr  attribute name
3112
	 * @param  bool     $val   attribute value
3113
	 * @return string|false
3114
	 * @author Dmitry (dio) Levashov
3115
	 **/
3116
	protected function childsByAttr($path, $attr, $val) {
3117
		foreach ($this->scandirCE($path) as $p) {
3118
			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...
3119
				return $_p;
3120
			}
3121
		}
3122
		return false;
3123
	}
3124
	
3125
	/*****************  get content *******************/
3126
	
3127
	/**
3128
	 * Return required dir's files info.
3129
	 * If onlyMimes is set - return only dirs and files of required mimes
3130
	 *
3131
	 * @param  string  $path  dir path
3132
	 * @return array
3133
	 * @author Dmitry (dio) Levashov
3134
	 **/
3135
	protected function getScandir($path) {
3136
		$files = array();
3137
		
3138
		!isset($this->dirsCache[$path]) && $this->cacheDir($path);
3139
3140
		foreach ($this->dirsCache[$path] as $p) {
3141
			if (($stat = $this->stat($p)) && empty($stat['hidden'])) {
3142
				$files[] = $stat;
3143
			}
3144
		}
3145
3146
		return $files;
3147
	}
3148
	
3149
	
3150
	/**
3151
	 * Return subdirs tree
3152
	 *
3153
	 * @param  string  $path  parent dir path
3154
	 * @param  int     $deep  tree deep
3155
	 * @return array
3156
	 * @author Dmitry (dio) Levashov
3157
	 **/
3158
	protected function gettree($path, $deep, $exclude='') {
3159
		$dirs = array();
3160
		
3161
		!isset($this->dirsCache[$path]) && $this->cacheDir($path);
3162
3163
		foreach ($this->dirsCache[$path] as $p) {
3164
			$stat = $this->stat($p);
3165
			
3166
			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...
3167
				$dirs[] = $stat;
3168
				if ($deep > 0 && !empty($stat['dirs'])) {
3169
					$dirs = array_merge($dirs, $this->gettree($p, $deep-1));
3170
				}
3171
			}
3172
		}
3173
3174
		return $dirs;
3175
	}	
3176
		
3177
	/**
3178
	 * Recursive files search
3179
	 *
3180
	 * @param  string  $path   dir path
3181
	 * @param  string  $q      search string
3182
	 * @param  array   $mimes
3183
	 * @return array
3184
	 * @author Dmitry (dio) Levashov
3185
	 **/
3186
	protected function doSearch($path, $q, $mimes) {
3187
		$result = array();
3188
3189
		foreach($this->scandirCE($path) as $p) {
3190
			@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...
3191
			$stat = $this->stat($p);
3192
3193
			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...
3194
				continue;
3195
			}
3196
3197
			if (!empty($stat['hidden']) || !$this->mimeAccepted($stat['mime'], $mimes)) {
3198
				continue;
3199
			}
3200
			
3201
			$name = $stat['name'];
3202
3203
			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...
3204
				$stat['path'] = $this->path($stat['hash']);
3205
				if ($this->URL && !isset($stat['url'])) {
3206
					$path = str_replace($this->separator, '/', substr($p, strlen($this->root) + 1));
3207
					if ($this->encoding) {
3208
						$path = str_replace('%2F', '/', rawurlencode($this->convEncIn($path, true)));
3209
					}
3210
					$stat['url'] = $this->URL . $path;
3211
				}
3212
				
3213
				$result[] = $stat;
3214
			}
3215 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...
3216
				$result = array_merge($result, $this->doSearch($p, $q, $mimes));
3217
			}
3218
		}
3219
		
3220
		return $result;
3221
	}
3222
		
3223
	/**********************  manuipulations  ******************/
3224
		
3225
	/**
3226
	 * Copy file/recursive copy dir only in current volume.
3227
	 * Return new file path or false.
3228
	 *
3229
	 * @param  string  $src   source path
3230
	 * @param  string  $dst   destination dir path
3231
	 * @param  string  $name  new file name (optionaly)
3232
	 * @return string|false
3233
	 * @author Dmitry (dio) Levashov
3234
	 **/
3235
	protected function copy($src, $dst, $name) {
3236
		$srcStat = $this->stat($src);
3237
		$this->clearcache();
3238
		
3239
		if (!empty($srcStat['thash'])) {
3240
			$target = $this->decode($srcStat['thash']);
3241
			if (!$this->inpathCE($target, $this->root)) {
3242
				return $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']), elFinder::ERROR_MKOUTLINK);
3243
			}
3244
			$stat   = $this->stat($target);
3245
			$this->clearcache();
3246
			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...
3247
				? $this->joinPathCE($dst, $name)
3248
				: $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']));
3249
		} 
3250
		
3251
		if ($srcStat['mime'] == 'directory') {
3252
			$test = $this->stat($this->joinPathCE($dst, $name));
3253
			
3254 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...
3255
				return $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']));
3256
			}
3257
			
3258
			$dst = $this->joinPathCE($dst, $name);
3259
			
3260
			foreach ($this->getScandir($src) as $stat) {
3261
				if (empty($stat['hidden'])) {
3262
					$name = $stat['name'];
3263
					if (!$this->copy($this->joinPathCE($src, $name), $dst, $name)) {
3264
						$this->remove($dst, true); // fall back
3265
						return $this->setError($this->error, elFinder::ERROR_COPY, $this->_path($src));
3266
					}
3267
				}
3268
			}
3269
			$this->clearcache();
3270
			return $dst;
3271
		} 
3272
3273
		return $this->convEncOut($this->_copy($this->convEncIn($src), $this->convEncIn($dst), $this->convEncIn($name)))
3274
			? $this->joinPathCE($dst, $name) 
3275
			: $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']));
3276
	}
3277
3278
	/**
3279
	 * Move file
3280
	 * Return new file path or false.
3281
	 *
3282
	 * @param  string  $src   source path
3283
	 * @param  string  $dst   destination dir path
3284
	 * @param  string  $name  new file name 
3285
	 * @return string|false
3286
	 * @author Dmitry (dio) Levashov
3287
	 **/
3288
	protected function move($src, $dst, $name) {
3289
		$stat = $this->stat($src);
3290
		$stat['realpath'] = $src;
3291
		$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...
3292
		$this->clearcache();
3293
		
3294 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...
3295
			$this->removed[] = $stat;
3296
3297
			return $this->joinPathCE($dst, $name);
3298
		}
3299
3300
		return $this->setError(elFinder::ERROR_MOVE, $this->path($stat['hash']));
3301
	}
3302
3303
	/**
3304
	 * Copy file from another volume.
3305
	 * Return new file path or false.
3306
	 *
3307
	 * @param  Object  $volume       source volume
3308
	 * @param  string  $src          source file hash
3309
	 * @param  string  $destination  destination dir path
3310
	 * @param  string  $name         file name
3311
	 * @return string|false
3312
	 * @author Dmitry (dio) Levashov
3313
	 **/
3314
	protected function copyFrom($volume, $src, $destination, $name) {
3315
		
3316
		if (($source = $volume->file($src)) == false) {
3317
			return $this->setError(elFinder::ERROR_COPY, '#'.$src, $volume->error());
3318
		}
3319
		
3320
		$errpath = $volume->path($source['hash']);
3321
		
3322
		if (!$this->nameAccepted($source['name'])) {
3323
			return $this->setError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_INVALID_NAME);
3324
		}
3325
				
3326
		if (!$source['read']) {
3327
			return $this->setError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_PERM_DENIED);
3328
		}
3329
		
3330
		if ($source['mime'] == 'directory') {
3331
			$stat = $this->stat($this->joinPathCE($destination, $name));
3332
			$this->clearcache();
3333 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...
3334
				return $this->setError(elFinder::ERROR_COPY, $errpath);
3335
			}
3336
			
3337
			$path = $this->joinPathCE($destination, $name);
3338
			
3339
			foreach ($volume->scandir($src) as $entr) {
3340
				if (!$this->copyFrom($volume, $entr['hash'], $path, $entr['name'])) {
3341
					$this->remove($path, true); // fall back
3342
					return $this->setError($this->error, elFinder::ERROR_COPY, $errpath);
3343
				}
3344
			}
3345
			
3346
		} else {
3347
			// $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...
3348
			// $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...
3349
			if (($dim = $volume->dimensions($src))) {
3350
				$s = explode('x', $dim);
3351
				$source['width']  = $s[0];
3352
				$source['height'] = $s[1];
3353
			}
3354
			
3355
			if (($fp = $volume->open($src)) == false
3356
			|| ($path = $this->saveCE($fp, $destination, $name, $source)) == false) {
3357
				$fp && $volume->close($fp, $src);
3358
				return $this->setError(elFinder::ERROR_COPY, $errpath);
3359
			}
3360
			$volume->close($fp, $src);
3361
			
3362
			// MIME check
3363
			$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 3356 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...
3364
			$mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($stat['name']);
3365
			if ($stat['mime'] === $mimeByName) {
3366
				$mimeByName = '';
3367
			}
3368 View Code Duplication
			if (!$this->allowPutMime($stat['mime']) || ($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...
3369
				$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 3356 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...
3370
				return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME, $errpath);
3371
			}
3372
		}
3373
		
3374
		return $path;
3375
	}
3376
		
3377
	/**
3378
	 * Remove file/ recursive remove dir
3379
	 *
3380
	 * @param  string  $path   file path
3381
	 * @param  bool    $force  try to remove even if file locked
3382
	 * @return bool
3383
	 * @author Dmitry (dio) Levashov
3384
	 **/
3385
	protected function remove($path, $force = false) {
3386
		$stat = $this->stat($path);
3387
		
3388
		if (empty($stat)) {
3389
			return $this->setError(elFinder::ERROR_RM, $this->path($stat['hash']), elFinder::ERROR_FILE_NOT_FOUND);
3390
		}
3391
		
3392
		$stat['realpath'] = $path;
3393
		$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...
3394
		$this->clearcache();
3395
		
3396 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...
3397
			return $this->setError(elFinder::ERROR_LOCKED, $this->path($stat['hash']));
3398
		}
3399
		
3400
		if ($stat['mime'] == 'directory' && empty($stat['thash'])) {
3401
			$ret = $this->delTree($this->convEncIn($path));
3402
			$this->convEncOut();
3403
			if (!$ret) {
3404
				return $this->setError(elFinder::ERROR_RM, $this->path($stat['hash']));
3405
			}
3406 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...
3407
			if ($this->convEncOut(!$this->_unlink($this->convEncIn($path)))) {
3408
				return $this->setError(elFinder::ERROR_RM, $this->path($stat['hash']));
3409
			}
3410
		}
3411
3412
		$this->removed[] = $stat;
3413
		return true;
3414
	}
3415
	
3416
3417
	/************************* thumbnails **************************/
3418
		
3419
	/**
3420
	 * Return thumbnail file name for required file
3421
	 *
3422
	 * @param  array  $stat  file stat
3423
	 * @return string
3424
	 * @author Dmitry (dio) Levashov
3425
	 **/
3426
	protected function tmbname($stat) {
3427
		return $stat['hash'].$stat['ts'].'.png';
3428
	}
3429
	
3430
	/**
3431
	 * Return thumnbnail name if exists
3432
	 *
3433
	 * @param  string  $path file path
3434
	 * @param  array   $stat file stat
3435
	 * @return string|false
3436
	 * @author Dmitry (dio) Levashov
3437
	 **/
3438
	protected function gettmb($path, $stat) {
3439
		if ($this->tmbURL && $this->tmbPath) {
3440
			// file itself thumnbnail
3441
			if (strpos($path, $this->tmbPath) === 0) {
3442
				return basename($path);
3443
			}
3444
3445
			$name = $this->tmbname($stat);
3446
			if (file_exists($this->tmbPath.DIRECTORY_SEPARATOR.$name)) {
3447
				return $name;
3448
			}
3449
		}
3450
		return false;
3451
	}
3452
	
3453
	/**
3454
	 * Return true if thumnbnail for required file can be created
3455
	 *
3456
	 * @param  string  $path  thumnbnail path 
3457
	 * @param  array   $stat  file stat
3458
	 * @param  bool    $checkTmbPath
3459
	 * @return string|bool
3460
	 * @author Dmitry (dio) Levashov
3461
	 **/
3462
	protected function canCreateTmb($path, $stat, $checkTmbPath = true) {
3463
		return (!$checkTmbPath || $this->tmbPathWritable) 
3464
			&& (!$this->tmbPath || strpos($path, $this->tmbPath) === false) // do not create thumnbnail for thumnbnail
3465
			&& $this->imgLib 
3466
			&& strpos($stat['mime'], 'image') === 0 
3467
			&& ($this->imgLib == 'gd' ? $stat['mime'] == 'image/jpeg' || $stat['mime'] == 'image/png' || $stat['mime'] == 'image/gif' : true);
3468
	}
3469
	
3470
	/**
3471
	 * Return true if required file can be resized.
3472
	 * By default - the same as canCreateTmb
3473
	 *
3474
	 * @param  string  $path  thumnbnail path 
3475
	 * @param  array   $stat  file stat
3476
	 * @return string|bool
3477
	 * @author Dmitry (dio) Levashov
3478
	 **/
3479
	protected function canResize($path, $stat) {
3480
		return $this->canCreateTmb($path, $stat, false);
3481
	}
3482
	
3483
	/**
3484
	 * Create thumnbnail and return it's URL on success
3485
	 *
3486
	 * @param  string  $path  file path
3487
	 * @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...
3488
	 * @return string|false
3489
	 * @author Dmitry (dio) Levashov
3490
	 **/
3491
	protected function createTmb($path, $stat) {
3492
		if (!$stat || !$this->canCreateTmb($path, $stat)) {
3493
			return false;
3494
		}
3495
3496
		$name = $this->tmbname($stat);
3497
		$tmb  = $this->tmbPath.DIRECTORY_SEPARATOR.$name;
3498
3499
		// copy image into tmbPath so some drivers does not store files on local fs
3500
		if (($src = $this->fopenCE($path, 'rb')) == false) {
3501
			return false;
3502
		}
3503
3504
		if (($trg = fopen($tmb, 'wb')) == false) {
3505
			$this->fcloseCE($src, $path);
3506
			return false;
3507
		}
3508
3509
		while (!feof($src)) {
3510
			fwrite($trg, fread($src, 8192));
3511
		}
3512
3513
		$this->fcloseCE($src, $path);
3514
		fclose($trg);
3515
3516
		$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...
3517
		
3518
		$tmbSize = $this->tmbSize;
3519
		
3520
		if (($s = getimagesize($tmb)) == false) {
3521
			return false;
3522
		}
3523
3524
		/* If image smaller or equal thumbnail size - just fitting to thumbnail square */
3525
		if ($s[0] <= $tmbSize && $s[1]	<= $tmbSize) {
3526
			$result = $this->imgSquareFit($tmb, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png' );
3527
		} else {
3528
		
3529
			if ($this->options['tmbCrop']) {
3530
		
3531
				$result = $tmb;
3532
				/* Resize and crop if image bigger than thumbnail */
3533 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...
3534
					$result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, false, 'png');
3535
				}
3536
		
3537 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...
3538
					$x = $s[0] > $tmbSize ? intval(($s[0] - $tmbSize)/2) : 0;
3539
					$y = $s[1] > $tmbSize ? intval(($s[1] - $tmbSize)/2) : 0;
3540
					$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...
3541
				} else {
3542
					$result = false;
3543
				}
3544
		
3545
			} else {
3546
				$result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, true, 'png');
3547
			}
3548
		
3549
			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...
3550
				$result = $this->imgSquareFit($result, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png' );
3551
			}
3552
		}
3553
		
3554
		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...
3555
			unlink($tmb);
3556
			return false;
3557
		}
3558
		
3559
		return $name;
3560
	}
3561
3562
	/**
3563
	 * Resize image
3564
	 *
3565
	 * @param  string   $path               image file
3566
	 * @param  int      $width              new width
3567
	 * @param  int      $height             new height
3568
	 * @param  bool	    $keepProportions    crop image
3569
	 * @param  bool	    $resizeByBiggerSide resize image based on bigger side if true
3570
	 * @param  string   $destformat         image destination format
3571
	 * @return string|false
3572
	 * @author Dmitry (dio) Levashov
3573
	 * @author Alexey Sukhotin
3574
	 **/
3575
	protected function imgResize($path, $width, $height, $keepProportions = false, $resizeByBiggerSide = true, $destformat = null) {
3576
		if (($s = @getimagesize($path)) == false) {
3577
			return false;
3578
		}
3579
3580
		$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...
3581
		
3582
		list($size_w, $size_h) = array($width, $height);
3583
	
3584
		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...
3585
		
3586
			list($orig_w, $orig_h) = array($s[0], $s[1]);
3587
		
3588
			/* Resizing by biggest side */
3589
			if ($resizeByBiggerSide) {
3590 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...
3591
					$size_h = round($orig_h * $width / $orig_w);
3592
					$size_w = $width;
3593
				} else {
3594
					$size_w = round($orig_w * $height / $orig_h);
3595
					$size_h = $height;
3596
				}
3597 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...
3598
				if ($orig_w > $orig_h) {
3599
					$size_w = round($orig_w * $height / $orig_h);
3600
					$size_h = $height;
3601
				} else {
3602
					$size_h = round($orig_h * $width / $orig_w);
3603
					$size_w = $width;
3604
				}
3605
			}
3606
		}
3607
3608
		switch ($this->imgLib) {
3609
			case 'imagick':
3610
				
3611
				try {
3612
					$img = new imagick($path);
3613
				} catch (Exception $e) {
3614
					return false;
3615
				}
3616
3617
				// Imagick::FILTER_BOX faster than FILTER_LANCZOS so use for createTmb
3618
				// 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...
3619
				// 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...
3620
				$filter = ($destformat === 'png' /* createTmb */)? Imagick::FILTER_BOX : Imagick::FILTER_LANCZOS;
3621
				
3622
				$ani = ($img->getNumberImages() > 1);
3623
				if ($ani && is_null($destformat)) {
3624
					$img = $img->coalesceImages();
3625
					do {
3626
						$img->resizeImage($size_w, $size_h, $filter, 1);
3627
					} while ($img->nextImage());
3628
					$img = $img->optimizeImageLayers();
3629
					$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...
3630
				} else {
3631
					if ($ani) {
3632
						$img->setFirstIterator();
3633
					}
3634
					$img->resizeImage($size_w, $size_h, $filter, 1);
3635
					$result = $img->writeImage($path);
3636
				}
3637
				
3638
				$img->destroy();
3639
3640
				return $result ? $path : false;
3641
3642
				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...
3643
3644
			case 'gd':
3645
				$img = self::gdImageCreate($path,$s['mime']);
3646
3647
				if ($img &&  false != ($tmp = imagecreatetruecolor($size_w, $size_h))) {
3648
				
3649
					self::gdImageBackground($tmp,$this->options['tmbBgColor']);
3650
					
3651
					if (!imagecopyresampled($tmp, $img, 0, 0, 0, 0, $size_w, $size_h, $s[0], $s[1])) {
3652
						return false;
3653
					}
3654
		
3655
					$result = self::gdImage($tmp, $path, $destformat, $s['mime']);
3656
3657
					imagedestroy($img);
3658
					imagedestroy($tmp);
3659
3660
					return $result ? $path : false;
3661
3662
				}
3663
				break;
3664
		}
3665
		
3666
		return false;
3667
  	}
3668
  
3669
	/**
3670
	 * Crop image
3671
	 *
3672
	 * @param  string   $path               image file
3673
	 * @param  int      $width              crop width
3674
	 * @param  int      $height             crop height
3675
	 * @param  bool	    $x                  crop left offset
3676
	 * @param  bool	    $y                  crop top offset
3677
	 * @param  string   $destformat         image destination format
3678
	 * @return string|false
3679
	 * @author Dmitry (dio) Levashov
3680
	 * @author Alexey Sukhotin
3681
	 **/
3682
  	protected function imgCrop($path, $width, $height, $x, $y, $destformat = null) {
3683
		if (($s = @getimagesize($path)) == false) {
3684
			return false;
3685
		}
3686
3687
		$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...
3688
		
3689
		switch ($this->imgLib) {
3690
			case 'imagick':
3691
				
3692
				try {
3693
					$img = new imagick($path);
3694
				} catch (Exception $e) {
3695
					return false;
3696
				}
3697
				
3698
				$ani = ($img->getNumberImages() > 1);
3699
				if ($ani && is_null($destformat)) {
3700
					$img = $img->coalesceImages();
3701
					do {
3702
						$img->setImagePage($s[0], $s[1], 0, 0);
3703
						$img->cropImage($width, $height, $x, $y);
3704
						$img->setImagePage($width, $height, 0, 0);
3705
					} while ($img->nextImage());
3706
					$img = $img->optimizeImageLayers();
3707
					$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...
3708
				} else {
3709
					if ($ani) {
3710
						$img->setFirstIterator();
3711
					}
3712
					$img->setImagePage($s[0], $s[1], 0, 0);
3713
					$img->cropImage($width, $height, $x, $y);
3714
					$img->setImagePage($width, $height, 0, 0);
3715
					$result = $img->writeImage($path);
3716
				}
3717
				
3718
				$img->destroy();
3719
3720
				return $result ? $path : false;
3721
3722
				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...
3723
3724
			case 'gd':
3725
				$img = self::gdImageCreate($path,$s['mime']);
3726
3727
				if ($img &&  false != ($tmp = imagecreatetruecolor($width, $height))) {
3728
					
3729
					self::gdImageBackground($tmp,$this->options['tmbBgColor']);
3730
3731
					$size_w = $width;
3732
					$size_h = $height;
3733
3734
					if ($s[0] < $width || $s[1] < $height) {
3735
						$size_w = $s[0];
3736
						$size_h = $s[1];
3737
					}
3738
3739
					if (!imagecopy($tmp, $img, 0, 0, $x, $y, $size_w, $size_h)) {
3740
						return false;
3741
					}
3742
					
3743
					$result = self::gdImage($tmp, $path, $destformat, $s['mime']);
3744
3745
					imagedestroy($img);
3746
					imagedestroy($tmp);
3747
3748
					return $result ? $path : false;
3749
3750
				}
3751
				break;
3752
		}
3753
3754
		return false;
3755
	}
3756
3757
	/**
3758
	 * Put image to square
3759
	 *
3760
	 * @param  string   $path               image file
3761
	 * @param  int      $width              square width
3762
	 * @param  int      $height             square height
3763
	 * @param  int	    $align              reserved
3764
	 * @param  int 	    $valign             reserved
3765
	 * @param  string   $bgcolor            square background color in #rrggbb format
3766
	 * @param  string   $destformat         image destination format
3767
	 * @return string|false
3768
	 * @author Dmitry (dio) Levashov
3769
	 * @author Alexey Sukhotin
3770
	 **/
3771
	protected function imgSquareFit($path, $width, $height, $align = 'center', $valign = 'middle', $bgcolor = '#0000ff', $destformat = 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...
3772
		if (($s = @getimagesize($path)) == false) {
3773
			return false;
3774
		}
3775
3776
		$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...
3777
3778
		/* Coordinates for image over square aligning */
3779
		$y = ceil(abs($height - $s[1]) / 2); 
3780
		$x = ceil(abs($width - $s[0]) / 2);
3781
3782
		switch ($this->imgLib) {
3783
			case 'imagick':
3784
				try {
3785
					$img = new imagick($path);
3786
				} catch (Exception $e) {
3787
					return false;
3788
				}
3789
				
3790
				$ani = ($img->getNumberImages() > 1);
3791
				if ($ani && is_null($destformat)) {
3792
					$img1 = new Imagick();
3793
					$img1->setFormat('gif');
3794
					$img = $img->coalesceImages();
3795
					do {
3796
						$gif = new Imagick();
3797
						$gif->newImage($width, $height, new ImagickPixel($bgcolor));
3798
						$gif->setImageColorspace($img->getImageColorspace());
3799
						$gif->setImageFormat('gif');
3800
						$gif->compositeImage( $img, imagick::COMPOSITE_OVER, $x, $y );
3801
						$gif->setImageDelay($img->getImageDelay());
3802
						$gif->setImageIterations($img->getImageIterations());
3803
						$img1->addImage($gif);
3804
						$gif->destroy();
3805
					} while ($img->nextImage());
3806
					$img1 = $img1->optimizeImageLayers();
3807
					$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...
3808
				} else {
3809
					if ($ani) {
3810
						$img->setFirstIterator();
3811
					}
3812
					$img1 = new Imagick();
3813
					$img1->newImage($width, $height, new ImagickPixel($bgcolor));
3814
					$img1->setImageColorspace($img->getImageColorspace());
3815
					$img1->setImageFormat($destformat != null ? $destformat : $img->getFormat());
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $destformat of type string|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison !== instead.
Loading history...
3816
					$img1->compositeImage( $img, imagick::COMPOSITE_OVER, $x, $y );
3817
					$result = $img1->writeImage($path);
3818
				}
3819
				
3820
				$img1->destroy();
3821
				$img->destroy();
3822
				return $result ? $path : false;
3823
3824
				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...
3825
3826
			case 'gd':
3827
				$img = self::gdImageCreate($path,$s['mime']);
3828
3829
				if ($img &&  false != ($tmp = imagecreatetruecolor($width, $height))) {
3830
3831
					self::gdImageBackground($tmp,$bgcolor);
3832
3833
					if (!imagecopy($tmp, $img, $x, $y, 0, 0, $s[0], $s[1])) {
3834
						return false;
3835
					}
3836
3837
					$result = self::gdImage($tmp, $path, $destformat, $s['mime']);
3838
3839
					imagedestroy($img);
3840
					imagedestroy($tmp);
3841
3842
					return $result ? $path : false;
3843
				}
3844
				break;
3845
		}
3846
3847
		return false;
3848
	}
3849
3850
	/**
3851
	 * Rotate image
3852
	 *
3853
	 * @param  string   $path               image file
3854
	 * @param  int      $degree             rotete degrees
3855
	 * @param  string   $bgcolor            square background color in #rrggbb format
3856
	 * @param  string   $destformat         image destination format
3857
	 * @return string|false
3858
	 * @author nao-pon
3859
	 * @author Troex Nevelin
3860
	 **/
3861
	protected function imgRotate($path, $degree, $bgcolor = '#ffffff', $destformat = null) {
3862
		if (($s = @getimagesize($path)) == false || $degree % 360 === 0) {
3863
			return false;
3864
		}
3865
3866
		$result = false;
3867
3868
		// try lossless rotate
3869
		if ($degree % 90 === 0 && in_array($s[2], array(IMAGETYPE_JPEG, IMAGETYPE_JPEG2000))) {
3870
			$count = ($degree / 90) % 4;
3871
			$exiftran = array(
3872
				1 => '-9',
3873
				2 => '-1',
3874
				3 => '-2'
3875
			);
3876
			$jpegtran = array(
3877
				1 => '90',
3878
				2 => '180',
3879
				3 => '270'
3880
			);
3881
			$quotedPath = escapeshellarg($path);
3882
			$cmds = array(
3883
				'exiftran -i '.$exiftran[$count].' '.$path,
3884
				'jpegtran -rotate '.$jpegtran[$count].' -copy all -outfile '.$quotedPath.' '.$quotedPath
3885
			);
3886
			foreach($cmds as $cmd) {
3887
				if ($this->procExec($cmd) === 0) {
3888
					$result = true;
3889
					break;
3890
				}
3891
			}
3892
			if ($result) {
3893
				return $path;
3894
			}
3895
		}
3896
3897
		switch ($this->imgLib) {
3898
			case 'imagick':
3899
				try {
3900
					$img = new imagick($path);
3901
				} catch (Exception $e) {
3902
					return false;
3903
				}
3904
3905
				if ($img->getNumberImages() > 1) {
3906
					$img = $img->coalesceImages();
3907
					do {
3908
						$img->rotateImage(new ImagickPixel($bgcolor), $degree);
3909
					} while ($img->nextImage());
3910
					$img = $img->optimizeImageLayers();
3911
					$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...
3912
				} else {
3913
					$img->rotateImage(new ImagickPixel($bgcolor), $degree);
3914
					$result = $img->writeImage($path);
3915
				}
3916
				$img->destroy();
3917
				return $result ? $path : false;
3918
3919
				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...
3920
3921
			case 'gd':
3922
				$img = self::gdImageCreate($path,$s['mime']);
3923
3924
				$degree = 360 - $degree;
3925
				list($r, $g, $b) = sscanf($bgcolor, "#%02x%02x%02x");
3926
				$bgcolor = imagecolorallocate($img, $r, $g, $b);
3927
				$tmp = imageRotate($img, $degree, (int)$bgcolor);
3928
3929
				$result = self::gdImage($tmp, $path, $destformat, $s['mime']);
3930
3931
				imageDestroy($img);
3932
				imageDestroy($tmp);
3933
3934
				return $result ? $path : false;
3935
3936
				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...
3937
		}
3938
3939
		return false;
3940
	}
3941
3942
	/**
3943
	 * Execute shell command
3944
	 *
3945
	 * @param  string  $command       command line
3946
	 * @param  array   $output        stdout strings
3947
	 * @param  array   $return_var    process exit code
3948
	 * @param  array   $error_output  stderr strings
3949
	 * @return int     exit code
3950
	 * @author Alexey Sukhotin
3951
	 **/
3952
	protected function procExec($command , array &$output = null, &$return_var = -1, array &$error_output = null) {
3953
3954
		$descriptorspec = array(
3955
			0 => array("pipe", "r"),  // stdin
3956
			1 => array("pipe", "w"),  // stdout
3957
			2 => array("pipe", "w")   // stderr
3958
		);
3959
3960
		$process = proc_open($command, $descriptorspec, $pipes, null, null);
3961
3962
		if (is_resource($process)) {
3963
3964
			fclose($pipes[0]);
3965
3966
			$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...
3967
			$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...
3968
3969
			$output = stream_get_contents($pipes[1]);
3970
			$error_output = stream_get_contents($pipes[2]);
3971
3972
			fclose($pipes[1]);
3973
			fclose($pipes[2]);
3974
			$return_var = proc_close($process);
3975
3976
3977
		}
3978
		
3979
		return $return_var;
3980
		
3981
	}
3982
3983
	/**
3984
	 * Remove thumbnail, also remove recursively if stat is directory
3985
	 *
3986
	 * @param  string  $stat  file stat
3987
	 * @return void
3988
	 * @author Dmitry (dio) Levashov
3989
	 * @author Naoki Sawada
3990
	 * @author Troex Nevelin
3991
	 **/
3992
	protected function rmTmb($stat) {
3993
		if ($stat['mime'] === 'directory') {
3994
			foreach ($this->scandirCE($this->decode($stat['hash'])) as $p) {
3995
				@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...
3996
				$name = $this->basenameCE($p);
3997
				$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...
3998
			}
3999
		} else if (!empty($stat['tmb']) && $stat['tmb'] != "1") {
4000
			$tmb = $this->tmbPath.DIRECTORY_SEPARATOR.$stat['tmb'];
4001
			file_exists($tmb) && @unlink($tmb);
4002
			clearstatcache();
4003
		}
4004
	}
4005
	
4006
	/**
4007
	 * Create an gd image according to the specified mime type
4008
	 *
4009
	 * @param string $path image file
4010
	 * @param string $mime
4011
	 * @return gd image resource identifier
4012
	 */
4013
	protected function gdImageCreate($path,$mime){
4014
		switch($mime){
4015
			case 'image/jpeg':
4016
			return imagecreatefromjpeg($path);
4017
4018
			case 'image/png':
4019
			return imagecreatefrompng($path);
4020
4021
			case 'image/gif':
4022
			return imagecreatefromgif($path);
4023
4024
			case 'image/xbm':
4025
			return imagecreatefromxbm($path);
4026
		}
4027
		return false;
4028
	}
4029
4030
	/**
4031
	 * Output gd image to file
4032
	 *
4033
	 * @param resource $image gd image resource
4034
	 * @param string $filename The path to save the file to.
4035
	 * @param string $destformat The Image type to use for $filename
4036
	 * @param string $mime The original image mime type
4037
	 */
4038
	protected function gdImage($image, $filename, $destformat, $mime ){
4039
4040
		if ($destformat == 'jpg' || ($destformat == null && $mime == 'image/jpeg')) {
4041
			return imagejpeg($image, $filename, 100);
4042
		}
4043
4044
		if ($destformat == 'gif' || ($destformat == null && $mime == 'image/gif')) {
4045
			return imagegif($image, $filename, 7);
4046
		}
4047
4048
		return imagepng($image, $filename, 7);
4049
	}
4050
4051
	/**
4052
	 * Assign the proper background to a gd image
4053
	 *
4054
	 * @param resource $image gd image resource
4055
	 * @param string $bgcolor background color in #rrggbb format
4056
	 */
4057
	protected function gdImageBackground($image, $bgcolor){
4058
4059
		if( $bgcolor == 'transparent' ){
4060
			imagesavealpha($image,true);
4061
			$bgcolor1 = imagecolorallocatealpha($image, 255, 255, 255, 127);
4062
4063
		}else{
4064
			list($r, $g, $b) = sscanf($bgcolor, "#%02x%02x%02x");
4065
			$bgcolor1 = imagecolorallocate($image, $r, $g, $b);
4066
		}
4067
4068
		imagefill($image, 0, 0, $bgcolor1);
4069
	}
4070
4071
	/*********************** misc *************************/
4072
	
4073
	/**
4074
	 * Return smart formatted date
4075
	 *
4076
	 * @param  int     $ts  file timestamp
4077
	 * @return string
4078
	 * @author Dmitry (dio) Levashov
4079
	 **/
4080
	// 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...
4081
	// 	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...
4082
	// 		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...
4083
	// 	}
4084
	// 	
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...
4085
	// 	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...
4086
	// 		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...
4087
	// 	} 
4088
	// 	
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...
4089
	// 	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...
4090
	// }
4091
4092
	/**
4093
	* Find position of first occurrence of string in a string with multibyte support
4094
	*
4095
	* @param  string  $haystack  The string being checked.
4096
	* @param  string  $needle    The string to find in haystack.
4097
	* @param  int     $offset    The search offset. If it is not specified, 0 is used.
4098
	* @return int|bool
4099
	* @author Alexey Sukhotin
4100
	**/
4101
	protected function stripos($haystack , $needle , $offset = 0) {
4102
		if (function_exists('mb_stripos')) {
4103
			return mb_stripos($haystack , $needle , $offset);
4104
		} else if (function_exists('mb_strtolower') && function_exists('mb_strpos')) {
4105
			return mb_strpos(mb_strtolower($haystack), mb_strtolower($needle), $offset);
4106
		} 
4107
		return stripos($haystack , $needle , $offset);
4108
	}
4109
4110
	/**
4111
	 * Get server side available archivers
4112
	 * 
4113
	 * @param bool $use_cache
4114
	 * @return array
4115
	 */
4116
	protected function getArchivers($use_cache = true) {
4117
4118
		$sessionKey = 'ARCHIVERS_CACHE';
4119
		if ($use_cache && isset($this->sessionCache[$sessionKey]) && is_array($this->sessionCache[$sessionKey])) {
4120
			return $this->sessionCache[$sessionKey];
4121
		}
4122
		
4123
		$arcs = array(
4124
			'create'  => array(),
4125
			'extract' => array()
4126
		);
4127
		
4128
		if (function_exists('proc_open')) {
4129
		
4130
			$this->procExec('tar --version', $o, $ctar);
4131
			
4132
			if ($ctar == 0) {
4133
				$arcs['create']['application/x-tar']  = array('cmd' => 'tar', 'argc' => '-cf', 'ext' => 'tar');
4134
				$arcs['extract']['application/x-tar'] = array('cmd' => 'tar', 'argc' => '-xf', 'ext' => 'tar');
4135
				unset($o);
4136
				$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...
4137 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...
4138
					$arcs['create']['application/x-gzip']  = array('cmd' => 'tar', 'argc' => '-czf', 'ext' => 'tgz');
4139
					$arcs['extract']['application/x-gzip'] = array('cmd' => 'tar', 'argc' => '-xzf', 'ext' => 'tgz');
4140
				}
4141
				unset($o);
4142
				$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...
4143 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...
4144
					$arcs['create']['application/x-bzip2']  = array('cmd' => 'tar', 'argc' => '-cjf', 'ext' => 'tbz');
4145
					$arcs['extract']['application/x-bzip2'] = array('cmd' => 'tar', 'argc' => '-xjf', 'ext' => 'tbz');
4146
				}
4147
				unset($o);
4148
				$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...
4149 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...
4150
					$arcs['create']['application/x-xz']  = array('cmd' => 'tar', 'argc' => '-cJf', 'ext' => 'xz');
4151
					$arcs['extract']['application/x-xz'] = array('cmd' => 'tar', 'argc' => '-xJf', 'ext' => 'xz');
4152
				}
4153
			}
4154
			unset($o);
4155
			$this->procExec('zip -v', $o, $c);
4156
			if ($c == 0) {
4157
				$arcs['create']['application/zip']  = array('cmd' => 'zip', 'argc' => '-r9', 'ext' => 'zip');
4158
			}
4159
			unset($o);
4160
			$this->procExec('unzip --help', $o, $c);
4161
			if ($c == 0) {
4162
				$arcs['extract']['application/zip'] = array('cmd' => 'unzip', 'argc' => '',  'ext' => 'zip');
4163
			}
4164
			unset($o);
4165
			$this->procExec('rar --version', $o, $c);
4166
			if ($c == 0 || $c == 7) {
4167
				$arcs['create']['application/x-rar']  = array('cmd' => 'rar', 'argc' => 'a -inul', 'ext' => 'rar');
4168
				$arcs['extract']['application/x-rar'] = array('cmd' => 'rar', 'argc' => 'x -y',    'ext' => 'rar');
4169
			} else {
4170
				unset($o);
4171
				$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...
4172
				if ($c==0 || $c == 7) {
4173
					$arcs['extract']['application/x-rar'] = array('cmd' => 'unrar', 'argc' => 'x -y', 'ext' => 'rar');
4174
				}
4175
			}
4176
			unset($o);
4177
			$this->procExec('7za --help', $o, $c);
4178
			if ($c == 0) {
4179
				$arcs['create']['application/x-7z-compressed']  = array('cmd' => '7za', 'argc' => 'a', 'ext' => '7z');
4180
				$arcs['extract']['application/x-7z-compressed'] = array('cmd' => '7za', 'argc' => 'x -y', 'ext' => '7z');
4181
				
4182
				if (empty($arcs['create']['application/zip'])) {
4183
					$arcs['create']['application/zip'] = array('cmd' => '7za', 'argc' => 'a -tzip', 'ext' => 'zip');
4184
				}
4185 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...
4186
					$arcs['extract']['application/zip'] = array('cmd' => '7za', 'argc' => 'x -tzip -y', 'ext' => 'zip');
4187
				}
4188
				if (empty($arcs['create']['application/x-tar'])) {
4189
					$arcs['create']['application/x-tar'] = array('cmd' => '7za', 'argc' => 'a -ttar', 'ext' => 'tar');
4190
				}
4191 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...
4192
					$arcs['extract']['application/x-tar'] = array('cmd' => '7za', 'argc' => 'x -ttar -y', 'ext' => 'tar');
4193
				}
4194
			} else if (substr(PHP_OS,0,3) === 'WIN') {
4195
				// check `7z` for Windows server.
4196
				unset($o);
4197
				$this->procExec('7z', $o, $c);
4198
				if ($c == 0) {
4199
					$arcs['create']['application/x-7z-compressed']  = array('cmd' => '7z', 'argc' => 'a', 'ext' => '7z');
4200
					$arcs['extract']['application/x-7z-compressed'] = array('cmd' => '7z', 'argc' => 'x -y', 'ext' => '7z');
4201
					
4202
					if (empty($arcs['create']['application/zip'])) {
4203
						$arcs['create']['application/zip'] = array('cmd' => '7z', 'argc' => 'a -tzip', 'ext' => 'zip');
4204
					}
4205
					if (empty($arcs['extract']['application/zip'])) {
4206
						$arcs['extract']['application/zip'] = array('cmd' => '7z', 'argc' => 'x -tzip -y', 'ext' => 'zip');
4207
					}
4208
					if (empty($arcs['create']['application/x-tar'])) {
4209
						$arcs['create']['application/x-tar'] = array('cmd' => '7z', 'argc' => 'a -ttar', 'ext' => 'tar');
4210
					}
4211
					if (empty($arcs['extract']['application/x-tar'])) {
4212
						$arcs['extract']['application/x-tar'] = array('cmd' => '7z', 'argc' => 'x -ttar -y', 'ext' => 'tar');
4213
					}
4214
				}
4215
			}
4216
		
4217
		}
4218
		
4219
		// Use PHP ZipArchive Class
4220
		if (class_exists('ZipArchive', false)) {
4221
			if (empty($arcs['create']['application/zip'])) {
4222
				$arcs['create']['application/zip']  = array('cmd' => 'phpfunction', 'argc' => 'self::zipArchiveZip', 'ext' => 'zip');
4223
			}
4224 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...
4225
				$arcs['extract']['application/zip'] = array('cmd' => 'phpfunction', 'argc' => 'self::zipArchiveUnzip', 'ext' => 'zip');
4226
			}
4227
		}
4228
		
4229
		$this->sessionCache[$sessionKey] = $arcs;
4230
		return $arcs;
4231
	}
4232
4233
	/**
4234
	 * Resolve relative / (Unix-like)absolute path
4235
	 * 
4236
	 * @param string $path  target path
4237
	 * @param string $base  base path
4238
	 * @return string
4239
	 */
4240
	protected function getFullPath($path, $base) {
4241
		$separator = $this->separator;
4242
		$systemroot = $this->systemRoot;
4243
4244
		$sepquoted = preg_quote($separator, '#');
4245
4246
		// normalize `/../`
4247
		$normreg = '#('.$sepquoted.')[^'.$sepquoted.']+'.$sepquoted.'\.\.'.$sepquoted.'#';
4248
		while(preg_match($normreg, $path)) {
4249
			$path = preg_replace($normreg, '$1', $path);
4250
		}
4251
		
4252
		// 'Here'
4253
		if ($path === '' || $path === '.' . $separator) return $base;
4254
		
4255
		// Absolute path
4256
		if ($path[0] === $separator || strpos($path, $systemroot) === 0) {
4257
			return $path;
4258
		}
4259
		
4260
		$preg_separator = '#' . $sepquoted . '#';
4261
		
4262
		// Relative path from 'Here'
4263
		if (substr($path, 0, 2) === '.' . $separator || $path[0] !== '.' || substr($path, 0, 3) !== '..' . $separator) {
4264
			$arrn = preg_split($preg_separator, $path, -1, PREG_SPLIT_NO_EMPTY);
4265
			if ($arrn[0] !== '.') {
4266
				array_unshift($arrn, '.');
4267
			}
4268
			$arrn[0] = $base;
4269
			return join($separator, $arrn);
4270
		}
4271
		
4272
		// Relative path from dirname()
4273
		if (substr($path, 0, 3) === '../') {
4274
			$arrn = preg_split($preg_separator, $path, -1, PREG_SPLIT_NO_EMPTY);
4275
			$arrp = preg_split($preg_separator, $base, -1, PREG_SPLIT_NO_EMPTY);
4276
		
4277
			while (! empty($arrn) && $arrn[0] === '..') {
4278
				array_shift($arrn);
4279
				array_pop($arrp);
4280
			}
4281
			$path = ! empty($arrp) ? $systemroot . join($separator, array_merge($arrp, $arrn)) :
4282
				(! empty($arrn) ? $systemroot . join($separator, $arrn) : $systemroot);
4283
		}
4284
		
4285
		return $path;
4286
	}
4287
4288
	/**
4289
	 * Remove directory recursive on local file system
4290
	 *
4291
	 * @param string $dir Target dirctory path
4292
	 * @return boolean
4293
	 * @author Naoki Sawada
4294
	 */
4295
	public function rmdirRecursive($dir) {
4296
		if (!is_link($dir) && is_dir($dir)) {
4297
			@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...
4298
			foreach (array_diff(scandir($dir), array('.', '..')) as $file) {
4299
				@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...
4300
				$path = $dir . DIRECTORY_SEPARATOR . $file;
4301
				if (!is_link($dir) && is_dir($path)) {
4302
					$this->rmdirRecursive($path);
4303
				} else {
4304
					@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...
4305
					@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...
4306
				}
4307
			}
4308
			return @rmdir($dir);
4309
		} else if (is_file($dir) || is_link($dir)) {
4310
			@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...
4311
			return @unlink($dir);
4312
		}
4313
		return false;
4314
	}
4315
4316
	/**
4317
	 * Create archive and return its path
4318
	 *
4319
	 * @param  string  $dir    target dir
4320
	 * @param  array   $files  files names list
4321
	 * @param  string  $name   archive name
4322
	 * @param  array   $arc    archiver options
4323
	 * @return string|bool
4324
	 * @author Dmitry (dio) Levashov, 
4325
	 * @author Alexey Sukhotin
4326
	 * @author Naoki Sawada
4327
	 **/
4328
	protected function makeArchive($dir, $files, $name, $arc) {
4329
		if ($arc['cmd'] === 'phpfunction') {
4330 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...
4331
				call_user_func_array($arc['argc'], array($dir, $files, $name));
4332
			}
4333
		} else {
4334
			$cwd = getcwd();
4335
			chdir($dir);
4336
			
4337
			$files = array_map('escapeshellarg', $files);
4338
			
4339
			$cmd = $arc['cmd'].' '.$arc['argc'].' '.escapeshellarg($name).' '.implode(' ', $files);
4340
			$this->procExec($cmd, $o, $c);
4341
			chdir($cwd);
4342
		}
4343
		$path = $dir.DIRECTORY_SEPARATOR.$name;
4344
		return file_exists($path) ? $path : false;
4345
	}
4346
	
4347
	/**
4348
	 * Unpack archive
4349
	 *
4350
	 * @param  string  $path   archive path
4351
	 * @param  array   $arc    archiver command and arguments (same as in $this->archivers)
4352
	 * @param  bool    $remove remove archive ( unlink($path) )
4353
	 * @return void
4354
	 * @author Dmitry (dio) Levashov
4355
	 * @author Alexey Sukhotin
4356
	 * @author Naoki Sawada
4357
	 **/
4358
	protected function unpackArchive($path, $arc, $remove = true) {
4359
		$dir = dirname($path);
4360
		if ($arc['cmd'] === 'phpfunction') {
4361 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...
4362
				call_user_func_array($arc['argc'], array($path, $dir));
4363
			}
4364
		} else {
4365
			$cwd = getcwd();
4366
			chdir($dir);
4367
			$cmd = $arc['cmd'].' '.$arc['argc'].' '.escapeshellarg(basename($path));
4368
			$this->procExec($cmd, $o, $c);
4369
			chdir($cwd);
4370
		}
4371
		$remove && unlink($path);
4372
	}
4373
	
4374
	/**
4375
	 * Create Zip archive using PHP class ZipArchive
4376
	 * 
4377
	 * @param  string        $dir      target dir
4378
	 * @param  array         $files    files names list
4379
	 * @param  string|object $zipPath  Zip archive name
4380
	 * @return void
4381
	 * @author Naoki Sawada
4382
	 */
4383
	protected static function zipArchiveZip($dir, $files, $zipPath) {
4384
		try {
4385
			if ($start = is_string($zipPath)) {
4386
				$zip = new ZipArchive();
4387
				if ($zip->open($dir . DIRECTORY_SEPARATOR . $zipPath, ZipArchive::CREATE) !== true) {
4388
					$zip = false;
4389
				}
4390
			} else {
4391
				$zip = $zipPath;
4392
			}
4393
			if ($zip) {
4394
				foreach($files as $file) {
4395
					$path = $dir . DIRECTORY_SEPARATOR . $file;
4396
					if (is_dir($path)) {
4397
						$zip->addEmptyDir($file);
4398
						$_files = array();
4399
						if ($handle = opendir($path)) {
4400
							while (false !== ($entry = readdir($handle))) {
4401
								if ($entry !== "." && $entry !== "..") {
4402
									$_files[] = $file . DIRECTORY_SEPARATOR . $entry;
4403
								}
4404
							}
4405
							closedir($handle);
4406
						}
4407
						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...
4408
							self::zipArchiveZip($dir, $_files, $zip);
4409
						}
4410
					} else {
4411
						$zip->addFile($path, $file);
4412
					}
4413
				}
4414
				$start && $zip->close();
4415
			}
4416
		} catch (Exception $e) {
4417
			return false;
4418
		}
4419
		return true;
4420
	}
4421
	
4422
	/**
4423
	 * Unpack Zip archive using PHP class ZipArchive
4424
	 * 
4425
	 * @param  string $zipPath  Zip archive name
4426
	 * @param  string $toDir    Extract to path
4427
	 * @return bool
4428
	 * @author Naoki Sawada
4429
	 */
4430
	protected static function zipArchiveUnzip($zipPath, $toDir) {
4431
		try {
4432
			$zip = new ZipArchive();
4433
			if ($zip->open($zipPath) === true) {
4434
				$zip->extractTo($toDir);
4435
				$zip->close();
4436
			}
4437
		} catch (Exception $e) {
4438
			return false;
4439
		}
4440
		return true;
4441
	}
4442
	
4443
	/**==================================* abstract methods *====================================**/
4444
	
4445
	/*********************** paths/urls *************************/
4446
	
4447
	/**
4448
	 * Return parent directory path
4449
	 *
4450
	 * @param  string  $path  file path
4451
	 * @return string
4452
	 * @author Dmitry (dio) Levashov
4453
	 **/
4454
	abstract protected function _dirname($path);
4455
4456
	/**
4457
	 * Return file name
4458
	 *
4459
	 * @param  string  $path  file path
4460
	 * @return string
4461
	 * @author Dmitry (dio) Levashov
4462
	 **/
4463
	abstract protected function _basename($path);
4464
4465
	/**
4466
	 * Join dir name and file name and return full path.
4467
	 * Some drivers (db) use int as path - so we give to concat path to driver itself
4468
	 *
4469
	 * @param  string  $dir   dir path
4470
	 * @param  string  $name  file name
4471
	 * @return string
4472
	 * @author Dmitry (dio) Levashov
4473
	 **/
4474
	abstract protected function _joinPath($dir, $name);
4475
4476
	/**
4477
	 * Return normalized path 
4478
	 *
4479
	 * @param  string  $path  file path
4480
	 * @return string
4481
	 * @author Dmitry (dio) Levashov
4482
	 **/
4483
	abstract protected function _normpath($path);
4484
4485
	/**
4486
	 * Return file path related to root dir
4487
	 *
4488
	 * @param  string  $path  file path
4489
	 * @return string
4490
	 * @author Dmitry (dio) Levashov
4491
	 **/
4492
	abstract protected function _relpath($path);
4493
	
4494
	/**
4495
	 * Convert path related to root dir into real path
4496
	 *
4497
	 * @param  string  $path  rel file path
4498
	 * @return string
4499
	 * @author Dmitry (dio) Levashov
4500
	 **/
4501
	abstract protected function _abspath($path);
4502
	
4503
	/**
4504
	 * Return fake path started from root dir.
4505
	 * Required to show path on client side.
4506
	 *
4507
	 * @param  string  $path  file path
4508
	 * @return string
4509
	 * @author Dmitry (dio) Levashov
4510
	 **/
4511
	abstract protected function _path($path);
4512
	
4513
	/**
4514
	 * Return true if $path is children of $parent
4515
	 *
4516
	 * @param  string  $path    path to check
4517
	 * @param  string  $parent  parent path
4518
	 * @return bool
4519
	 * @author Dmitry (dio) Levashov
4520
	 **/
4521
	abstract protected function _inpath($path, $parent);
4522
	
4523
	/**
4524
	 * Return stat for given path.
4525
	 * Stat contains following fields:
4526
	 * - (int)    size    file size in b. required
4527
	 * - (int)    ts      file modification time in unix time. required
4528
	 * - (string) mime    mimetype. required for folders, others - optionally
4529
	 * - (bool)   read    read permissions. required
4530
	 * - (bool)   write   write permissions. required
4531
	 * - (bool)   locked  is object locked. optionally
4532
	 * - (bool)   hidden  is object hidden. optionally
4533
	 * - (string) alias   for symlinks - link target path relative to root path. optionally
4534
	 * - (string) target  for symlinks - link target path. optionally
4535
	 *
4536
	 * If file does not exists - returns empty array or false.
4537
	 *
4538
	 * @param  string  $path    file path 
4539
	 * @return array|false
4540
	 * @author Dmitry (dio) Levashov
4541
	 **/
4542
	abstract protected function _stat($path);
4543
	
4544
4545
	/***************** file stat ********************/
4546
4547
		
4548
	/**
4549
	 * Return true if path is dir and has at least one childs directory
4550
	 *
4551
	 * @param  string  $path  dir path
4552
	 * @return bool
4553
	 * @author Dmitry (dio) Levashov
4554
	 **/
4555
	abstract protected function _subdirs($path);
4556
	
4557
	/**
4558
	 * Return object width and height
4559
	 * Ususaly used for images, but can be realize for video etc...
4560
	 *
4561
	 * @param  string  $path  file path
4562
	 * @param  string  $mime  file mime type
4563
	 * @return string
4564
	 * @author Dmitry (dio) Levashov
4565
	 **/
4566
	abstract protected function _dimensions($path, $mime);
4567
	
4568
	/******************** file/dir content *********************/
4569
4570
	/**
4571
	 * Return files list in directory
4572
	 *
4573
	 * @param  string  $path  dir path
4574
	 * @return array
4575
	 * @author Dmitry (dio) Levashov
4576
	 **/
4577
	abstract protected function _scandir($path);
4578
	
4579
	/**
4580
	 * Open file and return file pointer
4581
	 *
4582
	 * @param  string  $path  file path
4583
	 * @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...
4584
	 * @return resource|false
4585
	 * @author Dmitry (dio) Levashov
4586
	 **/
4587
	abstract protected function _fopen($path, $mode="rb");
4588
	
4589
	/**
4590
	 * Close opened file
4591
	 * 
4592
	 * @param  resource  $fp    file pointer
4593
	 * @param  string    $path  file path
4594
	 * @return bool
4595
	 * @author Dmitry (dio) Levashov
4596
	 **/
4597
	abstract protected function _fclose($fp, $path='');
4598
	
4599
	/********************  file/dir manipulations *************************/
4600
	
4601
	/**
4602
	 * Create dir and return created dir path or false on failed
4603
	 *
4604
	 * @param  string  $path  parent dir path
4605
	 * @param string  $name  new directory name
4606
	 * @return string|bool
4607
	 * @author Dmitry (dio) Levashov
4608
	 **/
4609
	abstract protected function _mkdir($path, $name);
4610
	
4611
	/**
4612
	 * Create file and return it's path or false on failed
4613
	 *
4614
	 * @param  string  $path  parent dir path
4615
	 * @param string  $name  new file name
4616
	 * @return string|bool
4617
	 * @author Dmitry (dio) Levashov
4618
	 **/
4619
	abstract protected function _mkfile($path, $name);
4620
	
4621
	/**
4622
	 * Create symlink
4623
	 *
4624
	 * @param  string  $source     file to link to
4625
	 * @param  string  $targetDir  folder to create link in
4626
	 * @param  string  $name       symlink name
4627
	 * @return bool
4628
	 * @author Dmitry (dio) Levashov
4629
	 **/
4630
	abstract protected function _symlink($source, $targetDir, $name);
4631
	
4632
	/**
4633
	 * Copy file into another file (only inside one volume)
4634
	 *
4635
	 * @param  string  $source  source file path
4636
	 * @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...
4637
	 * @param  string  $name    file name
4638
	 * @return bool
4639
	 * @author Dmitry (dio) Levashov
4640
	 **/
4641
	abstract protected function _copy($source, $targetDir, $name);
4642
	
4643
	/**
4644
	 * Move file into another parent dir.
4645
	 * Return new file path or false.
4646
	 *
4647
	 * @param  string  $source  source file path
4648
	 * @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...
4649
	 * @param  string  $name    file name
4650
	 * @return string|bool
4651
	 * @author Dmitry (dio) Levashov
4652
	 **/
4653
	abstract protected function _move($source, $targetDir, $name);
4654
	
4655
	/**
4656
	 * Remove file
4657
	 *
4658
	 * @param  string  $path  file path
4659
	 * @return bool
4660
	 * @author Dmitry (dio) Levashov
4661
	 **/
4662
	abstract protected function _unlink($path);
4663
4664
	/**
4665
	 * Remove dir
4666
	 *
4667
	 * @param  string  $path  dir path
4668
	 * @return bool
4669
	 * @author Dmitry (dio) Levashov
4670
	 **/
4671
	abstract protected function _rmdir($path);
4672
4673
	/**
4674
	 * Create new file and write into it from file pointer.
4675
	 * Return new file path or false on error.
4676
	 *
4677
	 * @param  resource  $fp   file pointer
4678
	 * @param  string    $dir  target dir path
4679
	 * @param  string    $name file name
4680
	 * @param  array     $stat file stat (required by some virtual fs)
4681
	 * @return bool|string
4682
	 * @author Dmitry (dio) Levashov
4683
	 **/
4684
	abstract protected function _save($fp, $dir, $name, $stat);
4685
	
4686
	/**
4687
	 * Get file contents
4688
	 *
4689
	 * @param  string  $path  file path
4690
	 * @return string|false
4691
	 * @author Dmitry (dio) Levashov
4692
	 **/
4693
	abstract protected function _getContents($path);
4694
	
4695
	/**
4696
	 * Write a string to a file
4697
	 *
4698
	 * @param  string  $path     file path
4699
	 * @param  string  $content  new file content
4700
	 * @return bool
4701
	 * @author Dmitry (dio) Levashov
4702
	 **/
4703
	abstract protected function _filePutContents($path, $content);
4704
4705
	/**
4706
	 * Extract files from archive
4707
	 *
4708
	 * @param  string  $path file path
4709
	 * @param  array   $arc  archiver options
4710
	 * @return bool
4711
	 * @author Dmitry (dio) Levashov, 
4712
	 * @author Alexey Sukhotin
4713
	 **/
4714
	abstract protected function _extract($path, $arc);
4715
4716
	/**
4717
	 * Create archive and return its path
4718
	 *
4719
	 * @param  string  $dir    target dir
4720
	 * @param  array   $files  files names list
4721
	 * @param  string  $name   archive name
4722
	 * @param  array   $arc    archiver options
4723
	 * @return string|bool
4724
	 * @author Dmitry (dio) Levashov, 
4725
	 * @author Alexey Sukhotin
4726
	 **/
4727
	abstract protected function _archive($dir, $files, $name, $arc);
4728
4729
	/**
4730
	 * Detect available archivers
4731
	 *
4732
	 * @return void
4733
	 * @author Dmitry (dio) Levashov, 
4734
	 * @author Alexey Sukhotin
4735
	 **/
4736
	abstract protected function _checkArchivers();
4737
4738
	/**
4739
	 * Change file mode (chmod)
4740
	 *
4741
	 * @param  string  $path  file path
4742
	 * @param  string  $mode  octal string such as '0755'
4743
	 * @return bool
4744
	 * @author David Bartle,
4745
	 **/
4746
	abstract protected function _chmod($path, $mode);
4747
4748
	
4749
} // END class
4750