Completed
Push — 2.x ( f14ff9...ad1156 )
by Naoki
03:15
created

elFinderVolumeDriver::symlinkCE()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 3
rs 10
cc 2
eloc 2
nc 2
nop 3
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
		$write = $file['write'];
671
		if (!$write && !$this->options['allowChmodReadOnly']) {
672
			return $this->setError(elFinder::ERROR_PERM_DENIED, $file['name']);
673
		}
674
675
		$path = $this->decode($hash);
676
677 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...
678
			return $this->setError(elFinder::ERROR_PERM_DENIED, $file['name']);
679
		}
680
681
		$this->clearcache();
682
683
		if ($file = $this->stat($path)) {
684
			$files = array($file);
685
			if ($file['mime'] === 'directory' && $write !== $file['write']) {
686
				foreach ($this->getScandir($path) as $stat) {
687
					if ($this->mimeAccepted($stat['mime'])) {
688
						$files[] = $stat;
689
					}
690
				}
691
			}
692
			return $files;
693
		} else {
694
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
695
		}
696
	}
697
	
698
	/**
699
	 * "Mount" volume.
700
	 * Return true if volume available for read or write, 
701
	 * false - otherwise
702
	 *
703
	 * @return bool
704
	 * @author Dmitry (dio) Levashov
705
	 * @author Alexey Sukhotin
706
	 **/
707
	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...
708
		if (!isset($opts['path']) || $opts['path'] === '') {
709
			return $this->setError('Path undefined.');;
710
		}
711
		
712
		$this->options = array_merge($this->options, $opts);
713
		$this->id = $this->driverId.(!empty($this->options['id']) ? $this->options['id'] : elFinder::$volumesCnt++).'_';
714
		$this->root = $this->normpathCE($this->options['path']);
715
		$this->separator = isset($this->options['separator']) ? $this->options['separator'] : DIRECTORY_SEPARATOR;
716
		$this->systemRoot = isset($this->options['systemRoot']) ? $this->options['systemRoot'] : DIRECTORY_SEPARATOR;
717
		
718
		// set server encoding
719
		if (!empty($this->options['encoding']) && strtoupper($this->options['encoding']) !== 'UTF-8') {
720
			$this->encoding = $this->options['encoding'];
721
		} else {
722
			$this->encoding = null;
723
		}
724
		
725
		$argInit = !empty($this->ARGS['init']);
726
		
727
		// session cache
728
		if ($argInit || ! isset($_SESSION[elFinder::$sessionCacheKey][$this->id])) {
729
			$_SESSION[elFinder::$sessionCacheKey][$this->id] = array();
730
		}
731
		$this->sessionCache = &$_SESSION[elFinder::$sessionCacheKey][$this->id];
732
		
733
		// default file attribute
734
		$this->defaults = array(
735
			'read'    => isset($this->options['defaults']['read'])  ? !!$this->options['defaults']['read']  : true,
736
			'write'   => isset($this->options['defaults']['write']) ? !!$this->options['defaults']['write'] : true,
737
			'locked'  => isset($this->options['defaults']['locked']) ? !!$this->options['defaults']['locked'] : false,
738
			'hidden'  => isset($this->options['defaults']['hidden']) ? !!$this->options['defaults']['hidden'] : false
739
		);
740
741
		// root attributes
742
		$this->attributes[] = array(
743
			'pattern' => '~^'.preg_quote(DIRECTORY_SEPARATOR).'$~',
744
			'locked'  => true,
745
			'hidden'  => false
746
		);
747
		// set files attributes
748 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...
749
			
750
			foreach ($this->options['attributes'] as $a) {
751
				// attributes must contain pattern and at least one rule
752
				if (!empty($a['pattern']) || count($a) > 1) {
753
					$this->attributes[] = $a;
754
				}
755
			}
756
		}
757
758
		if (!empty($this->options['accessControl']) && is_callable($this->options['accessControl'])) {
759
			$this->access = $this->options['accessControl'];
760
		}
761
		
762
		$this->today     = mktime(0,0,0, date('m'), date('d'), date('Y'));
763
		$this->yesterday = $this->today-86400;
764
		
765
		// 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...
766
		if (!$this->init()) {
767
			return false;
768
		}
769
		
770
		// check some options is arrays
771
		$this->uploadAllow = isset($this->options['uploadAllow']) && is_array($this->options['uploadAllow'])
772
			? $this->options['uploadAllow']
773
			: array();
774
			
775
		$this->uploadDeny = isset($this->options['uploadDeny']) && is_array($this->options['uploadDeny'])
776
			? $this->options['uploadDeny']
777
			: array();
778
779
		if (is_string($this->options['uploadOrder'])) { // telephat_mode on, compatibility with 1.x
780
			$parts = explode(',', isset($this->options['uploadOrder']) ? $this->options['uploadOrder'] : 'deny,allow');
781
			$this->uploadOrder = array(trim($parts[0]), trim($parts[1]));
782
		} else { // telephat_mode off
783
			$this->uploadOrder = $this->options['uploadOrder'];
784
		}
785
			
786
		if (!empty($this->options['uploadMaxSize'])) {
787
			$size = ''.$this->options['uploadMaxSize'];
788
			$unit = strtolower(substr($size, strlen($size) - 1));
789
			$n = 1;
790
			switch ($unit) {
791
				case 'k':
792
					$n = 1024;
793
					break;
794
				case 'm':
795
					$n = 1048576;
796
					break;
797
				case 'g':
798
					$n = 1073741824;
799
			}
800
			$this->uploadMaxSize = intval($size)*$n;
801
		}
802
		// Set maximum to PHP_INT_MAX
803
		if (!defined('PHP_INT_MAX')) {
804
			define('PHP_INT_MAX', 2147483647);
805
		}
806
		if ($this->uploadMaxSize < 1 || $this->uploadMaxSize > PHP_INT_MAX) {
807
			$this->uploadMaxSize = PHP_INT_MAX;
808
		}
809
		
810
		$this->disabled = isset($this->options['disabled']) && is_array($this->options['disabled'])
811
			? $this->options['disabled']
812
			: array();
813
		
814
		$this->cryptLib   = $this->options['cryptLib'];
815
		$this->mimeDetect = $this->options['mimeDetect'];
816
817
		// find available mimetype detect method
818
		$type = strtolower($this->options['mimeDetect']);
819
		$type = preg_match('/^(finfo|mime_content_type|internal|auto)$/i', $type) ? $type : 'auto';
820
		$regexp = '/text\/x\-(php|c\+\+)/';
821
	
822
		if (($type == 'finfo' || $type == 'auto') 
823
		&& class_exists('finfo')) {
824
			$tmpFileInfo = @explode(';', @finfo_file(finfo_open(FILEINFO_MIME), __FILE__));
825
		} else {
826
			$tmpFileInfo = false;
827
		}
828
	
829
		if ($tmpFileInfo && preg_match($regexp, array_shift($tmpFileInfo))) {
830
			$type = 'finfo';
831
			$this->finfo = finfo_open(FILEINFO_MIME);
832
		} elseif (($type == 'mime_content_type' || $type == 'auto') 
833
		&& function_exists('mime_content_type')
834
		&& 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...
835
			$type = 'mime_content_type';
836
		} else {
837
			$type = 'internal';
838
		}
839
		$this->mimeDetect = $type;
840
841
		// load mimes from external file for mimeDetect == 'internal'
842
		// based on Alexey Sukhotin idea and patch: http://elrte.org/redmine/issues/163
843
		// file must be in file directory or in parent one 
844
		if ($this->mimeDetect == 'internal' && !self::$mimetypesLoaded) {
845
			self::$mimetypesLoaded = true;
846
			$this->mimeDetect = 'internal';
847
			$file = false;
848
			if (!empty($this->options['mimefile']) && file_exists($this->options['mimefile'])) {
849
				$file = $this->options['mimefile'];
850
			} elseif (file_exists(dirname(__FILE__).DIRECTORY_SEPARATOR.'mime.types')) {
851
				$file = dirname(__FILE__).DIRECTORY_SEPARATOR.'mime.types';
852
			} elseif (file_exists(dirname(dirname(__FILE__)).DIRECTORY_SEPARATOR.'mime.types')) {
853
				$file = dirname(dirname(__FILE__)).DIRECTORY_SEPARATOR.'mime.types';
854
			}
855
856
			if ($file && file_exists($file)) {
857
				$mimecf = file($file);
858
859
				foreach ($mimecf as $line_num => $line) {
860
					if (!preg_match('/^\s*#/', $line)) {
861
						$mime = preg_split('/\s+/', $line, -1, PREG_SPLIT_NO_EMPTY);
862
						for ($i = 1, $size = count($mime); $i < $size ; $i++) {
863
							if (!isset(self::$mimetypes[$mime[$i]])) {
864
								self::$mimetypes[$mime[$i]] = $mime[0];
865
							}
866
						}
867
					}
868
				}
869
			}
870
		}
871
872
		$this->rootName = empty($this->options['alias']) ? $this->basenameCE($this->root) : $this->options['alias'];
873
874
		// This get's triggered if $this->root == '/' and alias is empty.
875
		// Maybe modify _basename instead?
876
		if ($this->rootName === '') $this->rootName = $this->separator;
877
878
		$root = $this->stat($this->root);
879
		
880
		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...
881
			return $this->setError('Root folder does not exists.');
882
		}
883
		if (!$root['read'] && !$root['write']) {
884
			return $this->setError('Root folder has not read and write permissions.');
885
		}
886
		
887
		// 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...
888
		
889
		if ($root['read']) {
890
			// check startPath - path to open by default instead of root
891
			if ($this->options['startPath']) {
892
				$start = $this->stat($this->options['startPath']);
893
				if (!empty($start)
894
				&& $start['mime'] == 'directory'
895
				&& $start['read']
896
				&& empty($start['hidden'])
897
				&& $this->inpathCE($this->options['startPath'], $this->root)) {
898
					$this->startPath = $this->options['startPath'];
899
					if (substr($this->startPath, -1, 1) == $this->options['separator']) {
900
						$this->startPath = substr($this->startPath, 0, -1);
901
					}
902
				}
903
			}
904
		} else {
905
			$this->options['URL']     = '';
906
			$this->options['tmbURL']  = '';
907
			$this->options['tmbPath'] = '';
908
			// read only volume
909
			array_unshift($this->attributes, array(
910
				'pattern' => '/.*/',
911
				'read'    => false
912
			));
913
		}
914
		$this->treeDeep = $this->options['treeDeep'] > 0 ? (int)$this->options['treeDeep'] : 1;
915
		$this->tmbSize  = $this->options['tmbSize'] > 0 ? (int)$this->options['tmbSize'] : 48;
916
		$this->URL      = $this->options['URL'];
917
		if ($this->URL && preg_match("|[^/?&=]$|", $this->URL)) {
918
			$this->URL .= '/';
919
		}
920
921
		$this->tmbURL   = !empty($this->options['tmbURL']) ? $this->options['tmbURL'] : '';
922
		if ($this->tmbURL && preg_match("|[^/?&=]$|", $this->tmbURL)) {
923
			$this->tmbURL .= '/';
924
		}
925
		
926
		$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...
927
			? $this->options['acceptedName']
928
			: '';
929
930
		$this->_checkArchivers();
931
		// manual control archive types to create
932 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...
933
			foreach ($this->archivers['create'] as $mime => $v) {
934
				if (!in_array($mime, $this->options['archiveMimes'])) {
935
					unset($this->archivers['create'][$mime]);
936
				}
937
			}
938
		}
939
		
940
		// manualy add archivers
941 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...
942
			foreach ($this->options['archivers']['create'] as $mime => $conf) {
943
				if (strpos($mime, 'application/') === 0 
944
				&& !empty($conf['cmd']) 
945
				&& isset($conf['argc']) 
946
				&& !empty($conf['ext'])
947
				&& !isset($this->archivers['create'][$mime])) {
948
					$this->archivers['create'][$mime] = $conf;
949
				}
950
			}
951
		}
952
		
953 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...
954
			foreach ($this->options['archivers']['extract'] as $mime => $conf) {
955
				if (strpos($mime, 'application/') === 0
956
				&& !empty($conf['cmd']) 
957
				&& isset($conf['argc']) 
958
				&& !empty($conf['ext'])
959
				&& !isset($this->archivers['extract'][$mime])) {
960
					$this->archivers['extract'][$mime] = $conf;
961
				}
962
			}
963
		}
964
965
		$this->configure();
966
		// 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...
967
		// 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...
968
		return $this->mounted = true;
969
	}
970
	
971
	/**
972
	 * Some "unmount" stuffs - may be required by virtual fs
973
	 *
974
	 * @return void
975
	 * @author Dmitry (dio) Levashov
976
	 **/
977
	public function umount() {
978
	}
979
	
980
	/**
981
	 * Return error message from last failed action
982
	 *
983
	 * @return array
984
	 * @author Dmitry (dio) Levashov
985
	 **/
986
	public function error() {
987
		return $this->error;
988
	}
989
	
990
	/**
991
	 * Return Extention/MIME Table (elFinderVolumeDriver::$mimetypes)
992
	 * 
993
	 * @return array
994
	 * @author Naoki Sawada
995
	 */
996
	public function getMimeTable() {
997
		return elFinderVolumeDriver::$mimetypes;
998
	}
999
	
1000
	/**
1001
	 * Set mimetypes allowed to display to client
1002
	 *
1003
	 * @param  array  $mimes
1004
	 * @return void
1005
	 * @author Dmitry (dio) Levashov
1006
	 **/
1007
	public function setMimesFilter($mimes) {
1008
		if (is_array($mimes)) {
1009
			$this->onlyMimes = $mimes;
1010
		}
1011
	}
1012
	
1013
	/**
1014
	 * Return root folder hash
1015
	 *
1016
	 * @return string
1017
	 * @author Dmitry (dio) Levashov
1018
	 **/
1019
	public function root() {
1020
		return $this->encode($this->root);
1021
	}
1022
	
1023
	/**
1024
	 * Return target path hash
1025
	 * 
1026
	 * @param  string $path
1027
	 * @param  string $name
1028
	 * @author Naoki Sawada
1029
	 */
1030
	public function getHash($path, $name = '') {
1031
		if ($name !== '') {
1032
			$path = $this->joinPathCE($path, $name);
1033
		}
1034
		return $this->encode($path);
1035
	}
1036
	
1037
	/**
1038
	 * Return root or startPath hash
1039
	 *
1040
	 * @return string
1041
	 * @author Dmitry (dio) Levashov
1042
	 **/
1043
	public function defaultPath() {
1044
		return $this->encode($this->startPath ? $this->startPath : $this->root);
1045
	}
1046
		
1047
	/**
1048
	 * Return volume options required by client:
1049
	 *
1050
	 * @return array
1051
	 * @author Dmitry (dio) Levashov
1052
	 **/
1053
	public function options($hash) {
1054
		$create = $createext = array();
1055
		if (isset($this->archivers['create']) && is_array($this->archivers['create'])) {
1056
			foreach($this->archivers['create'] as $m => $v) {
1057
				$create[] = $m;
1058
				$createext[$m] = $v['ext'];
1059
			}
1060
		}
1061
		return array(
1062
			'path'            => $this->path($hash),
1063
			'url'             => $this->URL,
1064
			'tmbUrl'          => $this->tmbURL,
1065
			'disabled'        => array_merge(array_unique($this->disabled)), // `array_merge` for type array of JSON
1066
			'separator'       => $this->separator,
1067
			'copyOverwrite'   => intval($this->options['copyOverwrite']),
1068
			'uploadOverwrite' => intval($this->options['uploadOverwrite']),
1069
			'uploadMaxSize'   => intval($this->uploadMaxSize),
1070
			'dispInlineRegex' => $this->options['dispInlineRegex'],
1071
			'archivers'       => array(
1072
				'create'    => $create,
1073
				'extract'   => isset($this->archivers['extract']) && is_array($this->archivers['extract']) ? array_keys($this->archivers['extract']) : array(),
1074
				'createext' => $createext
1075
			),
1076
			'uiCmdMap'        => (isset($this->options['uiCmdMap']) && is_array($this->options['uiCmdMap']))? $this->options['uiCmdMap'] : array()
1077
		);
1078
	}
1079
	
1080
	/**
1081
	 * Get option value of this volume
1082
	 * 
1083
	 * @param string $name  target option name
1084
	 * @return NULL|mixed   target option value
1085
	 * @author Naoki Sawada
1086
	 */
1087
	public function getOption($name) {
1088
		return isset($this->options[$name])? $this->options[$name] : null;
1089
	}
1090
	
1091
	/**
1092
	 * Get plugin values of this options
1093
	 * 
1094
	 * @param string $name  Plugin name
1095
	 * @return NULL|array   Plugin values
1096
	 * @author Naoki Sawada
1097
	 */
1098
	public function getOptionsPlugin($name = '') {
1099
		if ($name) {
1100
			return isset($this->options['plugin'][$name])? $this->options['plugin'][$name] : array();
1101
		} else {
1102
			return $this->options['plugin'];
1103
		}
1104
	}
1105
	
1106
	/**
1107
	 * Return true if command disabled in options
1108
	 *
1109
	 * @param  string  $cmd  command name
1110
	 * @return bool
1111
	 * @author Dmitry (dio) Levashov
1112
	 **/
1113
	public function commandDisabled($cmd) {
1114
		return in_array($cmd, $this->disabled);
1115
	}
1116
	
1117
	/**
1118
	 * Return true if mime is required mimes list
1119
	 *
1120
	 * @param  string     $mime   mime type to check
1121
	 * @param  array      $mimes  allowed mime types list or not set to use client mimes list
1122
	 * @param  bool|null  $empty  what to return on empty list
1123
	 * @return bool|null
1124
	 * @author Dmitry (dio) Levashov
1125
	 * @author Troex Nevelin
1126
	 **/
1127
	public function mimeAccepted($mime, $mimes = null, $empty = true) {
1128
		$mimes = is_array($mimes) ? $mimes : $this->onlyMimes;
1129
		if (empty($mimes)) {
1130
			return $empty;
1131
		}
1132
		return $mime == 'directory'
1133
			|| in_array('all', $mimes)
1134
			|| in_array('All', $mimes)
1135
			|| in_array($mime, $mimes)
1136
			|| in_array(substr($mime, 0, strpos($mime, '/')), $mimes);
1137
	}
1138
	
1139
	/**
1140
	 * Return true if voume is readable.
1141
	 *
1142
	 * @return bool
1143
	 * @author Dmitry (dio) Levashov
1144
	 **/
1145
	public function isReadable() {
1146
		$stat = $this->stat($this->root);
1147
		return $stat['read'];
1148
	}
1149
	
1150
	/**
1151
	 * Return true if copy from this volume allowed
1152
	 *
1153
	 * @return bool
1154
	 * @author Dmitry (dio) Levashov
1155
	 **/
1156
	public function copyFromAllowed() {
1157
		return !!$this->options['copyFrom'];
1158
	}
1159
	
1160
	/**
1161
	 * Return file path related to root with convert encoging
1162
	 *
1163
	 * @param  string   $hash  file hash
1164
	 * @return string
1165
	 * @author Dmitry (dio) Levashov
1166
	 **/
1167
	public function path($hash) {
1168
		return $this->convEncOut($this->_path($this->convEncIn($this->decode($hash))));
1169
	}
1170
	
1171
	/**
1172
	 * Return file real path if file exists
1173
	 *
1174
	 * @param  string  $hash  file hash
1175
	 * @return string
1176
	 * @author Dmitry (dio) Levashov
1177
	 **/
1178
	public function realpath($hash) {
1179
		$path = $this->decode($hash);
1180
		return $this->stat($path) ? $path : false;
1181
	}
1182
	
1183
	/**
1184
	 * Return list of moved/overwrited files
1185
	 *
1186
	 * @return array
1187
	 * @author Dmitry (dio) Levashov
1188
	 **/
1189
	public function removed() {
1190
		return $this->removed;
1191
	}
1192
	
1193
	/**
1194
	 * Clean removed files list
1195
	 *
1196
	 * @return void
1197
	 * @author Dmitry (dio) Levashov
1198
	 **/
1199
	public function resetRemoved() {
1200
		$this->removed = array();
1201
	}
1202
	
1203
	/**
1204
	 * Return file/dir hash or first founded child hash with required attr == $val
1205
	 *
1206
	 * @param  string   $hash  file hash
1207
	 * @param  string   $attr  attribute name
1208
	 * @param  bool     $val   attribute value
1209
	 * @return string|false
1210
	 * @author Dmitry (dio) Levashov
1211
	 **/
1212
	public function closest($hash, $attr, $val) {
1213
		return ($path = $this->closestByAttr($this->decode($hash), $attr, $val)) ? $this->encode($path) : false;
1214
	}
1215
	
1216
	/**
1217
	 * Return file info or false on error
1218
	 *
1219
	 * @param  string   $hash      file hash
1220
	 * @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...
1221
	 * @return array|false
1222
	 * @author Dmitry (dio) Levashov
1223
	 **/
1224
	public function file($hash) {
1225
		$path = $this->decode($hash);
1226
		
1227
		$file = $this->stat($path);
1228
		
1229
		if ($hash === $this->root()) {
1230
			$file['uiCmdMap'] = (isset($this->options['uiCmdMap']) && is_array($this->options['uiCmdMap']))? $this->options['uiCmdMap'] : array();
1231
			$file['disabled'] = array_merge(array_unique($this->disabled)); // `array_merge` for type array of JSON
1232
		}
1233
		
1234
		return ($file) ? $file : $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1235
	}
1236
	
1237
	/**
1238
	 * Return folder info
1239
	 *
1240
	 * @param  string   $hash  folder hash
1241
	 * @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...
1242
	 * @return array|false
1243
	 * @author Dmitry (dio) Levashov
1244
	 **/
1245
	public function dir($hash, $resolveLink=false) {
1246
		if (($dir = $this->file($hash)) == false) {
1247
			return $this->setError(elFinder::ERROR_DIR_NOT_FOUND);
1248
		}
1249
1250
		if ($resolveLink && !empty($dir['thash'])) {
1251
			$dir = $this->file($dir['thash']);
1252
		}
1253
		
1254
		return $dir && $dir['mime'] == 'directory' && empty($dir['hidden']) 
1255
			? $dir 
1256
			: $this->setError(elFinder::ERROR_NOT_DIR);
1257
	}
1258
	
1259
	/**
1260
	 * Return directory content or false on error
1261
	 *
1262
	 * @param  string   $hash   file hash
1263
	 * @return array|false
1264
	 * @author Dmitry (dio) Levashov
1265
	 **/
1266
	public function scandir($hash) {
1267
		if (($dir = $this->dir($hash)) == false) {
1268
			return false;
1269
		}
1270
		
1271
		return $dir['read']
1272
			? $this->getScandir($this->decode($hash))
1273
			: $this->setError(elFinder::ERROR_PERM_DENIED);
1274
	}
1275
1276
	/**
1277
	 * Return dir files names list
1278
	 * 
1279
	 * @param  string  $hash   file hash
1280
	 * @return array
1281
	 * @author Dmitry (dio) Levashov
1282
	 **/
1283
	public function ls($hash) {
1284 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...
1285
			return false;
1286
		}
1287
		
1288
		$list = array();
1289
		$path = $this->decode($hash);
1290
		
1291
		foreach ($this->getScandir($path) as $stat) {
1292
			if (empty($stat['hidden']) && $this->mimeAccepted($stat['mime'])) {
1293
				$list[] = $stat['name'];
1294
			}
1295
		}
1296
1297
		return $list;
1298
	}
1299
1300
	/**
1301
	 * Return subfolders for required folder or false on error
1302
	 *
1303
	 * @param  string   $hash  folder hash or empty string to get tree from root folder
1304
	 * @param  int      $deep  subdir deep
1305
	 * @param  string   $exclude  dir hash which subfolders must be exluded from result, required to not get stat twice on cwd subfolders
1306
	 * @return array|false
1307
	 * @author Dmitry (dio) Levashov
1308
	 **/
1309
	public function tree($hash='', $deep=0, $exclude='') {
1310
		$path = $hash ? $this->decode($hash) : $this->root;
1311
		
1312 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...
1313
			return false;
1314
		}
1315
		
1316
		$dirs = $this->gettree($path, $deep > 0 ? $deep -1 : $this->treeDeep-1, $exclude ? $this->decode($exclude) : null);
1317
		array_unshift($dirs, $dir);
1318
		return $dirs;
1319
	}
1320
	
1321
	/**
1322
	 * Return part of dirs tree from required dir up to root dir
1323
	 *
1324
	 * @param  string    $hash   directory hash
1325
	 * @param  bool|null $lineal only lineal parents
1326
	 * @return array
1327
	 * @author Dmitry (dio) Levashov
1328
	 **/
1329
	public function parents($hash, $lineal = false) {
1330
		if (($current = $this->dir($hash)) == false) {
1331
			return false;
1332
		}
1333
1334
		$path = $this->decode($hash);
1335
		$tree = array();
1336
		
1337
		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...
1338
			$path = $this->dirnameCE($path);
1339
			$stat = $this->stat($path);
1340
			if (!empty($stat['hidden']) || !$stat['read']) {
1341
				return false;
1342
			}
1343
			
1344
			array_unshift($tree, $stat);
1345
			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...
1346
				foreach ($this->gettree($path, 0) as $dir) {
1347
					if (!in_array($dir, $tree)) {
1348
						$tree[] = $dir;
1349
					}
1350
				}
1351
			}
1352
		}
1353
1354
		return $tree ? $tree : array($current);
1355
	}
1356
	
1357
	/**
1358
	 * Create thumbnail for required file and return its name of false on failed
1359
	 *
1360
	 * @return string|false
1361
	 * @author Dmitry (dio) Levashov
1362
	 **/
1363
	public function tmb($hash) {
1364
		$path = $this->decode($hash);
1365
		$stat = $this->stat($path);
1366
		
1367
		if (isset($stat['tmb'])) {
1368
			return $stat['tmb'] == "1" ? $this->createTmb($path, $stat) : $stat['tmb'];
1369
		}
1370
		return false;
1371
	}
1372
	
1373
	/**
1374
	 * Return file size / total directory size
1375
	 *
1376
	 * @param  string   file hash
1377
	 * @return int
1378
	 * @author Dmitry (dio) Levashov
1379
	 **/
1380
	public function size($hash) {
1381
		return $this->countSize($this->decode($hash));
1382
	}
1383
	
1384
	/**
1385
	 * Open file for reading and return file pointer
1386
	 *
1387
	 * @param  string   file hash
1388
	 * @return Resource
1389
	 * @author Dmitry (dio) Levashov
1390
	 **/
1391
	public function open($hash) {
1392 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...
1393
		|| $file['mime'] == 'directory') {
1394
			return false;
1395
		}
1396
		
1397
		return $this->fopenCE($this->decode($hash), 'rb');
1398
	}
1399
	
1400
	/**
1401
	 * Close file pointer
1402
	 *
1403
	 * @param  Resource  $fp   file pointer
1404
	 * @param  string    $hash file hash
1405
	 * @return void
1406
	 * @author Dmitry (dio) Levashov
1407
	 **/
1408
	public function close($fp, $hash) {
1409
		$this->fcloseCE($fp, $this->decode($hash));
1410
	}
1411
	
1412
	/**
1413
	 * Create directory and return dir info
1414
	 *
1415
	 * @param  string   $dsthash  destination directory hash
1416
	 * @param  string   $name directory name
1417
	 * @return array|false
1418
	 * @author Dmitry (dio) Levashov
1419
	 **/
1420
	public function mkdir($dsthash, $name) {
1421
		if ($this->commandDisabled('mkdir')) {
1422
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1423
		}
1424
		
1425
		if (!$this->nameAccepted($name)) {
1426
			return $this->setError(elFinder::ERROR_INVALID_NAME);
1427
		}
1428
		
1429
		if (($dir = $this->dir($dsthash)) == false) {
1430
			return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dsthash);
1431
		}
1432
		
1433
		$path = $this->decode($dsthash);
1434
		
1435
		if (!$dir['write'] || !$this->allowCreate($path, $name, true)) {
1436
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1437
		}
1438
		
1439
		$dst  = $this->joinPathCE($path, $name);
1440
		$stat = $this->stat($dst); 
1441
		if (!empty($stat)) { 
1442
			return $this->setError(elFinder::ERROR_EXISTS, $name);
1443
		}
1444
		$this->clearcache();
1445
		return ($path = $this->convEncOut($this->_mkdir($this->convEncIn($path), $this->convEncIn($name)))) ? $this->stat($path) : false;
1446
	}
1447
	
1448
	/**
1449
	 * Create empty file and return its info
1450
	 *
1451
	 * @param  string   $dst  destination directory
1452
	 * @param  string   $name file name
1453
	 * @return array|false
1454
	 * @author Dmitry (dio) Levashov
1455
	 **/
1456
	public function mkfile($dst, $name) {
1457
		if ($this->commandDisabled('mkfile')) {
1458
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1459
		}
1460
		
1461
		if (!$this->nameAccepted($name)) {
1462
			return $this->setError(elFinder::ERROR_INVALID_NAME);
1463
		}
1464
		
1465 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...
1466
			return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst);
1467
		}
1468
		
1469
		$path = $this->decode($dst);
1470
		
1471
		if (!$dir['write'] || !$this->allowCreate($path, $name, false)) {
1472
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1473
		}
1474
		
1475
		if ($this->stat($this->joinPathCE($path, $name))) {
1476
			return $this->setError(elFinder::ERROR_EXISTS, $name);
1477
		}
1478
		
1479
		$this->clearcache();
1480
		return ($path = $this->convEncOut($this->_mkfile($this->convEncIn($path), $this->convEncIn($name)))) ? $this->stat($path) : false;
1481
	}
1482
	
1483
	/**
1484
	 * Rename file and return file info
1485
	 *
1486
	 * @param  string  $hash  file hash
1487
	 * @param  string  $name  new file name
1488
	 * @return array|false
1489
	 * @author Dmitry (dio) Levashov
1490
	 **/
1491
	public function rename($hash, $name) {
1492
		if ($this->commandDisabled('rename')) {
1493
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1494
		}
1495
		
1496
		if (!$this->nameAccepted($name)) {
1497
			return $this->setError(elFinder::ERROR_INVALID_NAME, $name);
1498
		}
1499
		
1500
		$mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name);
1501 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...
1502
			return $this->setError(elFinder::ERROR_INVALID_NAME, $name);
1503
		}
1504
		
1505
		if (!($file = $this->file($hash))) {
1506
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1507
		}
1508
		
1509
		if ($name == $file['name']) {
1510
			return $file;
1511
		}
1512
		
1513
		if (!empty($file['locked'])) {
1514
			return $this->setError(elFinder::ERROR_LOCKED, $file['name']);
1515
		}
1516
		
1517
		$path = $this->decode($hash);
1518
		$dir  = $this->dirnameCE($path);
1519
		$stat = $this->stat($this->joinPathCE($dir, $name));
1520
		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...
1521
			return $this->setError(elFinder::ERROR_EXISTS, $name);
1522
		}
1523
		
1524 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...
1525
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1526
		}
1527
1528
		$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...
1529
1530
1531 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...
1532
			$this->clearcache();
1533
			return $this->stat($path);
1534
		}
1535
		return false;
1536
	}
1537
	
1538
	/**
1539
	 * Create file copy with suffix "copy number" and return its info
1540
	 *
1541
	 * @param  string   $hash    file hash
1542
	 * @param  string   $suffix  suffix to add to file name
1543
	 * @return array|false
1544
	 * @author Dmitry (dio) Levashov
1545
	 **/
1546
	public function duplicate($hash, $suffix='copy') {
1547
		if ($this->commandDisabled('duplicate')) {
1548
			return $this->setError(elFinder::ERROR_COPY, '#'.$hash, elFinder::ERROR_PERM_DENIED);
1549
		}
1550
		
1551
		if (($file = $this->file($hash)) == false) {
1552
			return $this->setError(elFinder::ERROR_COPY, elFinder::ERROR_FILE_NOT_FOUND);
1553
		}
1554
1555
		$path = $this->decode($hash);
1556
		$dir  = $this->dirnameCE($path);
1557
		$name = $this->uniqueName($dir, $this->basenameCE($path), ' '.$suffix.' ');
1558
1559 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...
1560
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1561
		}
1562
1563
		return ($path = $this->copy($path, $dir, $name)) == false
1564
			? false
1565
			: $this->stat($path);
0 ignored issues
show
Bug introduced by
It seems like $path defined by $this->copy($path, $dir, $name) on line 1563 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...
1566
	}
1567
	
1568
	/**
1569
	 * Save uploaded file. 
1570
	 * On success return array with new file stat and with removed file hash (if existed file was replaced)
1571
	 *
1572
	 * @param  Resource $fp      file pointer
1573
	 * @param  string   $dst     destination folder hash
1574
	 * @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...
1575
	 * @param  string   $tmpname file tmp name - required to detect mime type
1576
	 * @return array|false
1577
	 * @author Dmitry (dio) Levashov
1578
	 **/
1579
	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...
1580
		if ($this->commandDisabled('upload')) {
1581
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1582
		}
1583
		
1584 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...
1585
			return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst);
1586
		}
1587
1588
		if (!$dir['write']) {
1589
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1590
		}
1591
		
1592
		if (!$this->nameAccepted($name)) {
1593
			return $this->setError(elFinder::ERROR_INVALID_NAME);
1594
		}
1595
		
1596
		$mime = $this->mimetype($this->mimeDetect == 'internal' ? $name : $tmpname, $name);
1597
		$mimeByName = '';
1598
		if ($this->mimeDetect !== 'internal') {
1599
			$mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name);
1600
			if ($mime == 'unknown') {
1601
				$mime = $mimeByName;
1602
			}
1603
		}
1604
1605 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...
1606
			return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME);
1607
		}
1608
1609
		$tmpsize = sprintf('%u', filesize($tmpname));
1610
		if ($this->uploadMaxSize > 0 && $tmpsize > $this->uploadMaxSize) {
1611
			return $this->setError(elFinder::ERROR_UPLOAD_FILE_SIZE);
1612
		}
1613
1614
		$dstpath = $this->decode($dst);
1615
		$test    = $this->joinPathCE($dstpath, $name);
1616
		
1617
		$file = $this->stat($test);
1618
		$this->clearcache();
1619
		
1620
		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...
1621
			// check POST data `overwrite` for 3rd party uploader
1622
			$overwrite = isset($_POST['overwrite'])? (bool)$_POST['overwrite'] : $this->options['uploadOverwrite'];
1623
			if ($overwrite) {
1624 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...
1625
					return $this->setError(elFinder::ERROR_PERM_DENIED);
1626
				} elseif ($file['mime'] == 'directory') {
1627
					return $this->setError(elFinder::ERROR_NOT_REPLACE, $name);
1628
				} 
1629
				$this->remove($test);
1630
			} else {
1631
				$name = $this->uniqueName($dstpath, $name, '-', false);
1632
			}
1633
		}
1634
		
1635
		$stat = array(
1636
			'mime'   => $mime, 
1637
			'width'  => 0, 
1638
			'height' => 0, 
1639
			'size'   => $tmpsize);
1640
		
1641
		// $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...
1642
		if (strpos($mime, 'image') === 0 && ($s = getimagesize($tmpname))) {
1643
			$stat['width'] = $s[0];
1644
			$stat['height'] = $s[1];
1645
		}
1646
		// $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...
1647
		if (($path = $this->saveCE($fp, $dstpath, $name, $stat)) == false) {
1648
			return false;
1649
		}
1650
		
1651
		
1652
1653
		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 1647 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...
1654
	}
1655
	
1656
	/**
1657
	 * Paste files
1658
	 *
1659
	 * @param  Object  $volume  source volume
1660
	 * @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...
1661
	 * @param  string  $dst     destination dir hash
1662
	 * @param  bool    $rmSrc   remove source after copy?
1663
	 * @return array|false
1664
	 * @author Dmitry (dio) Levashov
1665
	 **/
1666
	public function paste($volume, $src, $dst, $rmSrc = false) {
1667
		$err = $rmSrc ? elFinder::ERROR_MOVE : elFinder::ERROR_COPY;
1668
		
1669
		if ($this->commandDisabled('paste')) {
1670
			return $this->setError($err, '#'.$src, elFinder::ERROR_PERM_DENIED);
1671
		}
1672
1673
		if (($file = $volume->file($src, $rmSrc)) == false) {
1674
			return $this->setError($err, '#'.$src, elFinder::ERROR_FILE_NOT_FOUND);
1675
		}
1676
1677
		$name = $file['name'];
1678
		$errpath = $volume->path($file['hash']);
1679
		
1680 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...
1681
			return $this->setError($err, $errpath, elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst);
1682
		}
1683
		
1684
		if (!$dir['write'] || !$file['read']) {
1685
			return $this->setError($err, $errpath, elFinder::ERROR_PERM_DENIED);
1686
		}
1687
1688
		$destination = $this->decode($dst);
1689
1690
		if (($test = $volume->closest($src, $rmSrc ? 'locked' : 'read', $rmSrc))) {
1691
			return $rmSrc
1692
				? $this->setError($err, $errpath, elFinder::ERROR_LOCKED, $volume->path($test))
1693
				: $this->setError($err, $errpath, !empty($file['thash'])? elFinder::ERROR_PERM_DENIED : elFinder::ERROR_MKOUTLINK);
1694
		}
1695
1696
		$test = $this->joinPathCE($destination, $name);
1697
		$stat = $this->stat($test);
1698
		$this->clearcache();
1699
		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...
1700
			if ($this->options['copyOverwrite']) {
1701
				// do not replace file with dir or dir with file
1702
				if (!$this->isSameType($file['mime'], $stat['mime'])) {
1703
					return $this->setError(elFinder::ERROR_NOT_REPLACE, $this->path($stat['hash']));
1704
				}
1705
				// existed file is not writable
1706
				if (!$stat['write']) {
1707
					return $this->setError($err, $errpath, elFinder::ERROR_PERM_DENIED);
1708
				}
1709
				// existed file locked or has locked child
1710
				if (($locked = $this->closestByAttr($test, 'locked', true))) {
1711
					$stat = $this->stat($locked);
1712
					return $this->setError(elFinder::ERROR_LOCKED, $this->path($stat['hash']));
1713
				}
1714
				// target is entity file of alias
1715
				if ($volume == $this && ($test == @$file['target'] || $test == $this->decode($src))) {
1716
					return $this->setError(elFinder::ERROR_REPLACE, $errpath);
1717
				}
1718
				// remove existed file
1719
				if (!$this->remove($test)) {
1720
					return $this->setError(elFinder::ERROR_REPLACE, $this->path($stat['hash']));
1721
				}
1722
			} else {
1723
				$name = $this->uniqueName($destination, $name, ' ', false);
1724
			}
1725
		}
1726
		
1727
		// copy/move inside current volume
1728
		if ($volume == $this) {
1729
			$source = $this->decode($src);
1730
			// do not copy into itself
1731
			if ($this->inpathCE($destination, $source)) {
1732
				return $this->setError(elFinder::ERROR_COPY_INTO_ITSELF, $errpath);
1733
			}
1734
			$method = $rmSrc ? 'move' : 'copy';
1735
			return ($path = $this->$method($source, $destination, $name)) ? $this->stat($path) : false;
1736
		}
1737
		
1738
		// copy/move from another volume
1739
		if (!$this->options['copyTo'] || !$volume->copyFromAllowed()) {
1740
			return $this->setError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_PERM_DENIED);
1741
		}
1742
		
1743
		if (($path = $this->copyFrom($volume, $src, $destination, $name)) == false) {
1744
			return false;
1745
		}
1746
		
1747
		if ($rmSrc) {
1748
			if (!$volume->rm($src)) {
1749
				return $this->setError(elFinder::ERROR_MOVE, $errpath, elFinder::ERROR_RM_SRC);
1750
			}
1751
		}
1752
		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 1743 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...
1753
	}
1754
	
1755
	/**
1756
	 * Return file contents
1757
	 *
1758
	 * @param  string  $hash  file hash
1759
	 * @return string|false
1760
	 * @author Dmitry (dio) Levashov
1761
	 **/
1762
	public function getContents($hash) {
1763
		$file = $this->file($hash);
1764
		
1765
		if (!$file) {
1766
			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...
1767
		}
1768
		
1769
		if ($file['mime'] == 'directory') {
1770
			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...
1771
		}
1772
		
1773
		if (!$file['read']) {
1774
			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...
1775
		}
1776
		
1777
		return $this->convEncOut($this->_getContents($this->convEncIn($this->decode($hash))));
1778
	}
1779
	
1780
	/**
1781
	 * Put content in text file and return file info.
1782
	 *
1783
	 * @param  string  $hash     file hash
1784
	 * @param  string  $content  new file content
1785
	 * @return array
1786
	 * @author Dmitry (dio) Levashov
1787
	 **/
1788
	public function putContents($hash, $content) {
1789
		if ($this->commandDisabled('edit')) {
1790
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1791
		}
1792
		
1793
		$path = $this->decode($hash);
1794
		
1795
		if (!($file = $this->file($hash))) {
1796
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1797
		}
1798
		
1799
		if (!$file['write']) {
1800
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1801
		}
1802
		
1803
		// check MIME
1804
		$name = $this->basenameCE($path);
1805
		$mime = '';
1806
		$mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name);
1807
		if ($this->mimeDetect !== 'internal') {
1808
			if ($tp = tmpfile()) {
1809
				fwrite($tp, $content);
1810
				$info = stream_get_meta_data($tp);
1811
				$filepath = $info['uri'];
1812
				$mime = $this->mimetype($filepath, $name);
1813
				fclose($tp);
1814
			}
1815
		}
1816 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...
1817
			return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME);
1818
		}
1819
		
1820
		$this->clearcache();
1821
		return $this->convEncOut($this->_filePutContents($this->convEncIn($path), $content)) ? $this->stat($path) : false;
1822
	}
1823
	
1824
	/**
1825
	 * Extract files from archive
1826
	 *
1827
	 * @param  string  $hash  archive hash
1828
	 * @return array|bool
1829
	 * @author Dmitry (dio) Levashov, 
1830
	 * @author Alexey Sukhotin
1831
	 **/
1832
	public function extract($hash, $makedir = null) {
1833
		if ($this->commandDisabled('extract')) {
1834
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1835
		}
1836
		
1837
		if (($file = $this->file($hash)) == false) {
1838
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1839
		}
1840
		
1841
		$archiver = isset($this->archivers['extract'][$file['mime']])
1842
			? $this->archivers['extract'][$file['mime']]
1843
			: false;
1844
			
1845
		if (!$archiver) {
1846
			return $this->setError(elFinder::ERROR_NOT_ARCHIVE);
1847
		}
1848
		
1849
		$path   = $this->decode($hash);
1850
		$parent = $this->stat($this->dirnameCE($path));
1851
1852
		if (!$file['read'] || !$parent['write']) {
1853
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1854
		}
1855
		$this->clearcache();
1856
		$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...
1857
		
1858
		if ($path = $this->convEncOut($this->_extract($this->convEncIn($path), $archiver))) {
1859
			if (is_array($path)) {
1860
				foreach ($path as $_k => $_p) {
1861
					$path[$_k] = $this->stat($_p);
1862
				}
1863
			} else {
1864
				$path = $this->stat($path);
1865
			}
1866
			return $path;
1867
		} else {
1868
			return false;
1869
		}
1870
	}
1871
1872
	/**
1873
	 * Add files to archive
1874
	 *
1875
	 * @return void
1876
	 **/
1877
	public function archive($hashes, $mime, $name = '') {
1878
		if ($this->commandDisabled('archive')) {
1879
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1880
		}
1881
1882
		$archiver = isset($this->archivers['create'][$mime])
1883
			? $this->archivers['create'][$mime]
1884
			: false;
1885
			
1886
		if (!$archiver) {
1887
			return $this->setError(elFinder::ERROR_ARCHIVE_TYPE);
1888
		}
1889
		
1890
		$files = array();
1891
		
1892
		foreach ($hashes as $hash) {
1893
			if (($file = $this->file($hash)) == false) {
1894
				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...
1895
			}
1896
			if (!$file['read']) {
1897
				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...
1898
			}
1899
			$path = $this->decode($hash);
1900
			if (!isset($dir)) {
1901
				$dir = $this->dirnameCE($path);
1902
				$stat = $this->stat($dir);
1903
				if (!$stat['write']) {
1904
					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...
1905
				}
1906
			}
1907
			
1908
			$files[] = $this->basenameCE($path);
1909
		}
1910
		
1911
		if ($name === '') {
1912
			$name = count($files) == 1 ? $files[0] : 'Archive';
1913
		} else {
1914
			$name = str_replace(array('/', '\\'), '_', preg_replace('/\.' . preg_quote($archiver['ext'], '/') . '$/i', '', $name));
1915
		}
1916
		$name .='.' . $archiver['ext'];
1917
		$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...
1918
		$this->clearcache();
1919
		return ($path = $this->convEncOut($this->_archive($this->convEncIn($dir), $this->convEncIn($files), $this->convEncIn($name), $archiver))) ? $this->stat($path) : false;
1920
	}
1921
	
1922
	/**
1923
	 * Resize image
1924
	 *
1925
	 * @param  string   $hash    image file
1926
	 * @param  int      $width   new width
1927
	 * @param  int      $height  new height
1928
	 * @param  int      $x       X start poistion for crop
1929
	 * @param  int      $y       Y start poistion for crop
1930
	 * @param  string   $mode    action how to mainpulate image
1931
	 * @return array|false
1932
	 * @author Dmitry (dio) Levashov
1933
	 * @author Alexey Sukhotin
1934
	 * @author nao-pon
1935
	 * @author Troex Nevelin
1936
	 **/
1937
	public function resize($hash, $width, $height, $x, $y, $mode = 'resize', $bg = '', $degree = 0) {
1938
		if ($this->commandDisabled('resize')) {
1939
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1940
		}
1941
		
1942
		if (($file = $this->file($hash)) == false) {
1943
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1944
		}
1945
		
1946
		if (!$file['write'] || !$file['read']) {
1947
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1948
		}
1949
		
1950
		$path = $this->decode($hash);
1951
		
1952
		$work_path = $this->getWorkFile($this->encoding? $this->convEncIn($path, true) : $path);
1953
1954
		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...
1955
			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...
1956
				@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...
1957
			}
1958
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1959
		}
1960
1961
		if ($this->imgLib != 'imagick') {
1962
			if (elFinder::isAnimationGif($work_path)) {
1963
				return $this->setError(elFinder::ERROR_UNSUPPORT_TYPE);
1964
			}
1965
		}
1966
1967
		switch($mode) {
1968
			
1969
			case 'propresize':
1970
				$result = $this->imgResize($work_path, $width, $height, true, true);
1971
				break;
1972
1973
			case 'crop':
1974
				$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...
1975
				break;
1976
1977
			case 'fitsquare':
1978
				$result = $this->imgSquareFit($work_path, $width, $height, 'center', 'middle', ($bg ? $bg : $this->options['tmbBgColor']));
1979
				break;
1980
1981
			case 'rotate':
1982
				$result = $this->imgRotate($work_path, $degree, ($bg ? $bg : $this->options['tmbBgColor']));
1983
				break;
1984
1985
			default:
1986
				$result = $this->imgResize($work_path, $width, $height, false, true);
1987
				break;
1988
		}
1989
		
1990
		$ret = false;
1991
		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...
1992
			$stat = $this->stat($path);
1993
			clearstatcache();
1994
			$fstat = stat($work_path);
1995
			$stat['size'] = $fstat['size'];
1996
			$stat['ts'] = $fstat['mtime'];
1997
			if ($imgsize = @getimagesize($work_path)) {
1998
				$stat['width'] = $imgsize[0];
1999
				$stat['height'] = $imgsize[1];
2000
				$stat['mime'] = $imgsize['mime'];
2001
			}
2002
			if ($path !== $work_path) {
2003
				if ($fp = @fopen($work_path, 'rb')) {
2004
					$ret = $this->saveCE($fp, $this->dirnameCE($path), $this->basenameCE($path), $stat);
2005
					@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...
2006
				}
2007
			} else {
2008
				$ret = true;
2009
			}
2010
			if ($ret) {
2011
				$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...
2012
				$this->clearcache();
2013
				$ret = $this->stat($path);
2014
				$ret['width'] = $stat['width'];
2015
				$ret['height'] = $stat['height'];
2016
			}
2017
		}
2018
		if ($path !== $work_path) {
2019
			is_file($work_path) && @unlink($work_path);
2020
		}
2021
		
2022
		return $ret;
2023
	}
2024
	
2025
	/**
2026
	 * Remove file/dir
2027
	 *
2028
	 * @param  string  $hash  file hash
2029
	 * @return bool
2030
	 * @author Dmitry (dio) Levashov
2031
	 **/
2032
	public function rm($hash) {
2033
		return $this->commandDisabled('rm')
2034
			? $this->setError(elFinder::ERROR_PERM_DENIED)
2035
			: $this->remove($this->decode($hash));
2036
	}
2037
	
2038
	/**
2039
	 * Search files
2040
	 *
2041
	 * @param  string  $q  search string
2042
	 * @param  array   $mimes
2043
	 * @return array
2044
	 * @author Dmitry (dio) Levashov
2045
	 **/
2046
	public function search($q, $mimes, $hash = null) {
2047
		$dir = null;
2048
		if ($hash) {
2049
			$dir = $this->decode($hash);
2050
			$stat = $this->stat($dir);
2051
			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...
2052
				$q = '';
2053
			}
2054
		}
2055
		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...
2056
			$mimes = array_intersect($mimes, $this->onlyMimes);
2057
			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...
2058
				$q = '';
2059
			}
2060
		}
2061
		return ($q === '' || $this->commandDisabled('search'))
2062
			? array()
2063
			: $this->doSearch(is_null($dir)? $this->root : $dir, $q, $mimes);
2064
	}
2065
	
2066
	/**
2067
	 * Return image dimensions
2068
	 *
2069
	 * @param  string  $hash  file hash
2070
	 * @return array
2071
	 * @author Dmitry (dio) Levashov
2072
	 **/
2073
	public function dimensions($hash) {
2074
		if (($file = $this->file($hash)) == false) {
2075
			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...
2076
		}
2077
		
2078
		return $this->convEncOut($this->_dimensions($this->convEncIn($this->decode($hash)), $file['mime']));
2079
	}
2080
	
2081
	/**
2082
	 * Return content URL (for netmout volume driver)
2083
	 * If file.url == 1 requests from JavaScript client with XHR
2084
	 * 
2085
	 * @param string $hash  file hash
2086
	 * @param array $options  options array
2087
	 * @return boolean|string
2088
	 * @author Naoki Sawada
2089
	 */
2090
	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...
2091 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...
2092
			return false;
2093
		}
2094
		return $file['url'];
2095
	}
2096
	
2097
	/**
2098
	 * Return temp path
2099
	 * 
2100
	 * @return string
2101
	 * @author Naoki Sawada
2102
	 */
2103
	public function getTempPath() {
2104
		if (@ $this->tmpPath) {
2105
			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...
2106
		} else if (@ $this->tmp) {
2107
			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...
2108
		} else if (function_exists('sys_get_temp_dir')) {
2109
			return sys_get_temp_dir();
2110
		} else if (@ $this->tmbPath) {
2111
			return $this->tmbPath;
2112
		} else {
2113
			return null;
2114
		}
2115
	}
2116
	
2117
	/**
2118
	 * (Make &) Get upload taget dirctory hash
2119
	 * 
2120
	 * @param string $baseTargetHash
2121
	 * @param string $path
2122
	 * @param array  $result
2123
	 * @return boolean|string
2124
	 * @author Naoki Sawada
2125
	 */
2126
	public function getUploadTaget($baseTargetHash, $path, & $result) {
2127
		$base = $this->decode($baseTargetHash);
2128
		$targetHash = $baseTargetHash;
2129
		$path = ltrim($path, $this->separator);
2130
		$dirs = explode($this->separator, $path);
2131
		array_pop($dirs);
2132
		foreach($dirs as $dir) {
2133
			$targetPath = $this->joinPathCE($base, $dir);
2134
			if (! $_realpath = $this->realpath($this->encode($targetPath))) {
2135
				if ($stat = $this->mkdir($targetHash, $dir)) {
2136
					$result['added'][] = $stat;
2137
					$targetHash = $stat['hash'];
2138
					$base = $this->decode($targetHash);
2139
				} else {
2140
					return false;
2141
				}
2142
			} else {
2143
				$targetHash = $this->encode($_realpath);
2144
				if ($this->dir($targetHash)) {
2145
					$base = $this->decode($targetHash);
2146
				} else {
2147
					return false;
2148
				}
2149
			}
2150
		}
2151
		return $targetHash;
2152
	}
2153
	
2154
	/**
2155
	 * Return this uploadMaxSize value
2156
	 * 
2157
	 * @return integer
2158
	 * @author Naoki Sawada
2159
	 */
2160
	public function getUploadMaxSize() {
2161
		return $this->uploadMaxSize;
2162
	}
2163
	
2164
	/**
2165
	 * Save error message
2166
	 *
2167
	 * @param  array  error 
2168
	 * @return false
2169
	 * @author Dmitry(dio) Levashov
2170
	 **/
2171
	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...
2172
		
2173
		$this->error = array();
2174
		
2175
		foreach (func_get_args() as $err) {
2176
			if (is_array($err)) {
2177
				$this->error = array_merge($this->error, $err);
2178
			} else {
2179
				$this->error[] = $err;
2180
			}
2181
		}
2182
		
2183
		// $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...
2184
		return false;
2185
	}
2186
	
2187
	/*********************************************************************/
2188
	/*                               FS API                              */
2189
	/*********************************************************************/
2190
	
2191
	/***************** server encoding support *******************/
2192
	
2193
	/**
2194
	 * Return parent directory path (with convert encording)
2195
	 *
2196
	 * @param  string  $path  file path
2197
	 * @return string
2198
	 * @author Naoki Sawada
2199
	 **/
2200
	protected function dirnameCE($path) {
2201
		return (!$this->encoding)? $this->_dirname($path) :	$this->convEncOut($this->_dirname($this->convEncIn($path)));
2202
	}
2203
	
2204
	/**
2205
	 * Return file name (with convert encording)
2206
	 *
2207
	 * @param  string  $path  file path
2208
	 * @return string
2209
	 * @author Naoki Sawada
2210
	 **/
2211
	protected function basenameCE($path) {
2212
		return (!$this->encoding)? $this->_basename($path) : $this->convEncOut($this->_basename($this->convEncIn($path)));
2213
	}
2214
	
2215
	/**
2216
	 * Join dir name and file name and return full path. (with convert encording)
2217
	 * Some drivers (db) use int as path - so we give to concat path to driver itself
2218
	 *
2219
	 * @param  string  $dir   dir path
2220
	 * @param  string  $name  file name
2221
	 * @return string
2222
	 * @author Naoki Sawada
2223
	 **/
2224
	protected function joinPathCE($dir, $name) {
2225
		return (!$this->encoding)? $this->_joinPath($dir, $name) : $this->convEncOut($this->_joinPath($this->convEncIn($dir), $this->convEncIn($name)));
2226
	}
2227
	
2228
	/**
2229
	 * Return normalized path (with convert encording)
2230
	 *
2231
	 * @param  string  $path  file path
2232
	 * @return string
2233
	 * @author Naoki Sawada
2234
	 **/
2235
	protected function normpathCE($path) {
2236
		return (!$this->encoding)? $this->_normpath($path) : $this->convEncOut($this->_normpath($this->convEncIn($path)));
2237
	}
2238
	
2239
	/**
2240
	 * Return file path related to root dir (with convert encording)
2241
	 *
2242
	 * @param  string  $path  file path
2243
	 * @return string
2244
	 * @author Naoki Sawada
2245
	 **/
2246
	protected function relpathCE($path) {
2247
		return (!$this->encoding)? $this->_relpath($path) : $this->convEncOut($this->_relpath($this->convEncIn($path)));
2248
	}
2249
	
2250
	/**
2251
	 * Convert path related to root dir into real path (with convert encording)
2252
	 *
2253
	 * @param  string  $path  rel file path
2254
	 * @return string
2255
	 * @author Naoki Sawada
2256
	 **/
2257
	protected function abspathCE($path) {
2258
		return (!$this->encoding)? $this->_abspath($path): $this->convEncOut($this->_abspath($this->convEncIn($path)));
2259
	}
2260
	
2261
	/**
2262
	 * Return true if $path is children of $parent (with convert encording)
2263
	 *
2264
	 * @param  string  $path    path to check
2265
	 * @param  string  $parent  parent path
2266
	 * @return bool
2267
	 * @author Naoki Sawada
2268
	 **/
2269
	protected function inpathCE($path, $parent) {
2270
		return (!$this->encoding)? $this->_inpath($path, $parent) : $this->convEncOut($this->_inpath($this->convEncIn($path), $this->convEncIn($parent)));
2271
	}
2272
	
2273
	/**
2274
	 * Open file and return file pointer (with convert encording)
2275
	 *
2276
	 * @param  string  $path  file path
2277
	 * @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...
2278
	 * @return resource|false
2279
	 * @author Naoki Sawada
2280
	 **/
2281
	protected function fopenCE($path, $mode='rb') {
2282
		return (!$this->encoding)? $this->_fopen($path, $mode) : $this->convEncOut($this->_fopen($this->convEncIn($path), $mode));
2283
	}
2284
	
2285
	/**
2286
	 * Close opened file (with convert encording)
2287
	 * 
2288
	 * @param  resource  $fp    file pointer
2289
	 * @param  string    $path  file path
2290
	 * @return bool
2291
	 * @author Naoki Sawada
2292
	 **/
2293
	protected function fcloseCE($fp, $path='') {
2294
		return (!$this->encoding)? $this->_fclose($fp, $path) : $this->convEncOut($this->_fclose($fp, $this->convEncIn($path)));
2295
	}
2296
	
2297
	/**
2298
	 * Create new file and write into it from file pointer. (with convert encording)
2299
	 * Return new file path or false on error.
2300
	 *
2301
	 * @param  resource  $fp   file pointer
2302
	 * @param  string    $dir  target dir path
2303
	 * @param  string    $name file name
2304
	 * @param  array     $stat file stat (required by some virtual fs)
2305
	 * @return bool|string
2306
	 * @author Naoki Sawada
2307
	 **/
2308
	protected function saveCE($fp, $dir, $name, $stat) {
2309
		return (!$this->encoding)? $this->_save($fp, $dir, $name, $stat) : $this->convEncOut($this->_save($fp, $this->convEncIn($dir), $this->convEncIn($name), $this->convEncIn($stat)));
2310
	}
2311
	
2312
	/**
2313
	 * Return true if path is dir and has at least one childs directory (with convert encording)
2314
	 *
2315
	 * @param  string  $path  dir path
2316
	 * @return bool
2317
	 * @author Naoki Sawada
2318
	 **/
2319
	protected function subdirsCE($path) {
2320
		if (!isset($this->subdirsCache[$path])) {
2321
			$this->subdirsCache[$path] = (!$this->encoding)? $this->_subdirs($path) : $this->convEncOut($this->_subdirs($this->convEncIn($path)));
2322
		}
2323
		return $this->subdirsCache[$path];
2324
	}
2325
	
2326
	/**
2327
	 * Return files list in directory (with convert encording)
2328
	 *
2329
	 * @param  string  $path  dir path
2330
	 * @return array
2331
	 * @author Naoki Sawada
2332
	 **/
2333
	protected function scandirCE($path) {
2334
		return (!$this->encoding)? $this->_scandir($path) : $this->convEncOut($this->_scandir($this->convEncIn($path)));
2335
	}
2336
	
2337
	/**
2338
	 * Create symlink (with convert encording)
2339
	 *
2340
	 * @param  string  $source     file to link to
2341
	 * @param  string  $targetDir  folder to create link in
2342
	 * @param  string  $name       symlink name
2343
	 * @return bool
2344
	 * @author Naoki Sawada
2345
	 **/
2346
	protected function symlinkCE($source, $targetDir, $name) {
2347
		return (!$this->encoding)? $this->_symlink($source, $targetDir, $name) : $this->convEncOut($this->_symlink($this->convEncIn($source), $this->convEncIn($targetDir), $this->convEncIn($name)));
2348
	}
2349
	
2350
	/***************** paths *******************/
2351
	
2352
	/**
2353
	 * Encode path into hash
2354
	 *
2355
	 * @param  string  file path
2356
	 * @return string
2357
	 * @author Dmitry (dio) Levashov
2358
	 * @author Troex Nevelin
2359
	 **/
2360
	protected function encode($path) {
2361
		if ($path !== '') {
2362
2363
			// cut ROOT from $path for security reason, even if hacker decodes the path he will not know the root
2364
			$p = $this->relpathCE($path);
2365
			// if reqesting root dir $path will be empty, then assign '/' as we cannot leave it blank for crypt
2366
			if ($p === '')	{
2367
				$p = DIRECTORY_SEPARATOR;
2368
			}
2369
2370
			// TODO crypt path and return hash
2371
			$hash = $this->crypt($p);
2372
			// hash is used as id in HTML that means it must contain vaild chars
2373
			// make base64 html safe and append prefix in begining
2374
			$hash = strtr(base64_encode($hash), '+/=', '-_.');
2375
			// remove dots '.' at the end, before it was '=' in base64
2376
			$hash = rtrim($hash, '.'); 
2377
			// append volume id to make hash unique
2378
			return $this->id.$hash;
2379
		}
2380
	}
2381
	
2382
	/**
2383
	 * Decode path from hash
2384
	 *
2385
	 * @param  string  file hash
2386
	 * @return string
2387
	 * @author Dmitry (dio) Levashov
2388
	 * @author Troex Nevelin
2389
	 **/
2390
	protected function decode($hash) {
2391
		if (strpos($hash, $this->id) === 0) {
2392
			// cut volume id after it was prepended in encode
2393
			$h = substr($hash, strlen($this->id));
2394
			// replace HTML safe base64 to normal
2395
			$h = base64_decode(strtr($h, '-_.', '+/='));
2396
			// TODO uncrypt hash and return path
2397
			$path = $this->uncrypt($h); 
2398
			// append ROOT to path after it was cut in encode
2399
			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...
2400
		}
2401
	}
2402
	
2403
	/**
2404
	 * Return crypted path 
2405
	 * Not implemented
2406
	 *
2407
	 * @param  string  path
2408
	 * @return mixed
2409
	 * @author Dmitry (dio) Levashov
2410
	 **/
2411
	protected function crypt($path) {
2412
		return $path;
2413
	}
2414
	
2415
	/**
2416
	 * Return uncrypted path 
2417
	 * Not implemented
2418
	 *
2419
	 * @param  mixed  hash
2420
	 * @return mixed
2421
	 * @author Dmitry (dio) Levashov
2422
	 **/
2423
	protected function uncrypt($hash) {
2424
		return $hash;
2425
	}
2426
	
2427
	/**
2428
	 * Validate file name based on $this->options['acceptedName'] regexp or function
2429
	 *
2430
	 * @param  string  $name  file name
2431
	 * @return bool
2432
	 * @author Dmitry (dio) Levashov
2433
	 **/
2434
	protected function nameAccepted($name) {
2435
		if ($this->nameValidator) {
2436
			if (is_callable($this->nameValidator)) {
2437
				$res = call_user_func($this->nameValidator, $name);
2438
				return $res;
2439
			}
2440
			if (preg_match($this->nameValidator, '') !== false) {
2441
				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...
2442
			}
2443
		}
2444
		return true;
2445
	}
2446
	
2447
	/**
2448
	 * Return new unique name based on file name and suffix
2449
	 *
2450
	 * @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...
2451
	 * @param  string  $suffix  suffix append to name
2452
	 * @return string
2453
	 * @author Dmitry (dio) Levashov
2454
	 **/
2455
	public function uniqueName($dir, $name, $suffix = ' copy', $checkNum = true, $start = 1) {
2456
		$ext  = '';
2457
2458
		if (preg_match('/\.((tar\.(gz|bz|bz2|z|lzo))|cpio\.gz|ps\.gz|xcf\.(gz|bz2)|[a-z0-9]{1,4})$/i', $name, $m)) {
2459
			$ext  = '.'.$m[1];
2460
			$name = substr($name, 0,  strlen($name)-strlen($m[0]));
2461
		} 
2462
		
2463
		if ($checkNum && preg_match('/('.preg_quote($suffix, '/').')(\d*)$/i', $name, $m)) {
2464
			$i    = (int)$m[2];
2465
			$name = substr($name, 0, strlen($name)-strlen($m[2]));
2466
		} else {
2467
			$i     = $start;
2468
			$name .= $suffix;
2469
		}
2470
		$max = $i+100000;
2471
2472
		while ($i <= $max) {
2473
			$n = $name.($i > 0 ? $i : '').$ext;
2474
2475
			if (!$this->stat($this->joinPathCE($dir, $n))) {
2476
				$this->clearcache();
2477
				return $n;
2478
			}
2479
			$i++;
2480
		}
2481
		return $name.md5($dir).$ext;
2482
	}
2483
	
2484
	/**
2485
	 * Converts character encoding from UTF-8 to server's one
2486
	 * 
2487
	 * @param  mixed  $var           target string or array var
2488
	 * @param  bool   $restoreLocale do retore global locale, default is false
2489
	 * @param  string $unknown       replaces character for unknown
2490
	 * @return mixed
2491
	 * @author Naoki Sawada
2492
	 */
2493
	public function convEncIn($var = null, $restoreLocale = false, $unknown = '_') {
2494
		return (!$this->encoding)? $var : $this->convEnc($var, 'UTF-8', $this->encoding, $this->options['locale'], $restoreLocale, $unknown);
2495
	}
2496
	
2497
	/**
2498
	 * Converts character encoding from server's one to UTF-8
2499
	 * 
2500
	 * @param  mixed  $var           target string or array var
2501
	 * @param  bool   $restoreLocale do retore global locale, default is true
2502
	 * @param  string $unknown       replaces character for unknown
2503
	 * @return mixed
2504
	 * @author Naoki Sawada
2505
	 */
2506
	public function convEncOut($var = null, $restoreLocale = true, $unknown = '_') {
2507
		return (!$this->encoding)? $var : $this->convEnc($var, $this->encoding, 'UTF-8', $this->options['locale'], $restoreLocale, $unknown);
2508
	}
2509
	
2510
	/**
2511
	 * Converts character encoding (base function)
2512
	 * 
2513
	 * @param  mixed  $var     target string or array var
2514
	 * @param  string $from    from character encoding
2515
	 * @param  string $to      to character encoding
2516
	 * @param  string $locale  local locale
2517
	 * @param  string $unknown replaces character for unknown
2518
	 * @return mixed
2519
	 */
2520
	protected function convEnc($var, $from, $to, $locale, $restoreLocale, $unknown = '_') {
2521
		if (strtoupper($from) !== strtoupper($to)) {
2522
			if ($locale) {
2523
				@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...
2524
			}
2525
			if (is_array($var)) {
2526
				$_ret = array();
2527
				foreach($var as $_k => $_v) {
2528
					$_ret[$_k] = $this->convEnc($_v, $from, $to, '', false, $unknown = '_');
2529
				}
2530
				$var = $_ret;
2531
			} else {
2532
				$_var = false;
2533
				if (is_string($var)) {
2534
					$_var = $var;
2535
					if (false !== ($_var = @iconv($from, $to.'//TRANSLIT', $_var))) {
2536
						$_var = str_replace('?', $unknown, $_var);
2537
					}
2538
				}
2539
				if  ($_var !== false) {
2540
					$var = $_var;
2541
				}
2542
			}
2543
			if ($restoreLocale) {
2544
				setlocale(LC_ALL, elFinder::$locale);
2545
			}
2546
		}
2547
		return $var;
2548
	}
2549
	
2550
	/*********************** util mainly for inheritance class *********************/
2551
	
2552
	/**
2553
	 * Get temporary filename. Tempfile will be removed when after script execution finishes or exit() is called.
2554
	 * When needing the unique file to a path, give $path to parameter.
2555
	 * 
2556
	 * @param  string       $path for get unique file to a path
2557
	 * @return string|false
2558
	 * @author Naoki Sawada
2559
	 */
2560
	protected function getTempFile($path = '') {
2561
		static $cache = array();
2562
		static $rmfunc;
2563
		
2564
		$key = '';
2565
		if ($path !== '') {
2566
			$key = $this->id . '#' . $path;
2567
			if (isset($cache[$key])) {
2568
				return $cache[$key];
2569
			}
2570
		}
2571
		
2572
		if ($tmpdir = $this->getTempPath()) {
2573
			if (!$rmfunc) {
2574
				$rmfunc = create_function('$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...
2575
			}
2576
			$name = tempnam($tmpdir, 'ELF');
2577
			if ($key) {
2578
				$cache[$key] = $name;
2579
			}
2580
			register_shutdown_function($rmfunc, $name);
2581
			return $name;
2582
		}
2583
		
2584
		return false;
2585
	}
2586
	
2587
	/**
2588
	 * File path of local server side work file path
2589
	 * 
2590
	 * @param  string $path path need convert encoding to server encoding
2591
	 * @return string
2592
	 * @author Naoki Sawada
2593
	 */
2594
	protected function getWorkFile($path) {
2595
		if ($work = $this->getTempFile()) {
2596
			if ($wfp = fopen($work, 'wb')) {
2597
				if ($fp = $this->_fopen($path)) {
2598
					while(!feof($fp)) {
2599
						fwrite($wfp, fread($fp, 8192));
2600
					}
2601
					$this->_fclose($fp, $path);
2602
					fclose($wfp);
2603
					return $work;
2604
				}
2605
			}
2606
		}
2607
		return false;
2608
	}
2609
	
2610
	/**
2611
	 * Get image size array with `dimensions`
2612
	 *
2613
	 * @param string $path path need convert encoding to server encoding
2614
	 * @param string $mime file mime type
2615
	 * @return array|false
2616
	 */
2617
	public function getImageSize($path, $mime = '') {
2618
		$size = false;
2619
		if ($mime === '' || strtolower(substr($mime, 0, 5)) === 'image') {
2620
			if ($work = $this->getWorkFile($path)) {
2621
				if ($size = @getimagesize($work)) {
2622
					$size['dimensions'] = $size[0].'x'.$size[1];
2623
				}
2624
			}
2625
			is_file($work) && @unlink($work);
2626
		}
2627
		return $size;
2628
	}
2629
	
2630
	/**
2631
	 * Delete dirctory trees
2632
	 *
2633
	 * @param string $localpath path need convert encoding to server encoding
2634
	 * @return boolean
2635
	 * @author Naoki Sawada
2636
	 */
2637
	protected function delTree($localpath) {
2638
		foreach ($this->_scandir($localpath) as $p) {
2639
			@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...
2640
			$stat = $this->stat($this->convEncOut($p));
2641
			$this->convEncIn();
2642
			($stat['mime'] === 'directory')? $this->delTree($p) : $this->_unlink($p);
2643
		}
2644
		return $this->_rmdir($localpath);
2645
	}
2646
	
2647
	/*********************** file stat *********************/
2648
	
2649
	/**
2650
	 * Check file attribute
2651
	 *
2652
	 * @param  string  $path  file path
2653
	 * @param  string  $name  attribute name (read|write|locked|hidden)
2654
	 * @param  bool    $val   attribute value returned by file system
2655
	 * @param  bool    $isDir path is directory (true: directory, false: file)
2656
	 * @return bool
2657
	 * @author Dmitry (dio) Levashov
2658
	 **/
2659
	protected function attr($path, $name, $val=null, $isDir=null) {
2660
		if (!isset($this->defaults[$name])) {
2661
			return false;
2662
		}
2663
		
2664
		
2665
		$perm = null;
2666
		
2667 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...
2668
			$perm = call_user_func($this->access, $name, $path, $this->options['accessControlData'], $this, $isDir);
2669
2670
			if ($perm !== null) {
2671
				return !!$perm;
2672
			}
2673
		}
2674
		
2675
		if ($this->separator != '/') {
2676
			$path = str_replace($this->separator, '/', $this->relpathCE($path));
2677
		} else {
2678
			$path = $this->relpathCE($path);
2679
		}
2680
2681
		$path = '/'.$path;
2682
2683 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...
2684
			$attrs = $this->attributes[$i];
2685
			
2686
			if (isset($attrs[$name]) && isset($attrs['pattern']) && preg_match($attrs['pattern'], $path)) {
2687
				$perm = $attrs[$name];
2688
			} 
2689
		}
2690
		
2691
		return $perm === null ? (is_null($val)? $this->defaults[$name] : $val) : !!$perm;
2692
	}
2693
	
2694
	/**
2695
	 * Return true if file with given name can be created in given folder.
2696
	 *
2697
	 * @param string $dir  parent dir path
2698
	 * @param string $name new file name
2699
	 * @return bool
2700
	 * @author Dmitry (dio) Levashov
2701
	 **/
2702
	protected function allowCreate($dir, $name, $isDir = null) {
2703
		$path = $this->joinPathCE($dir, $name);
2704
		$perm = null;
2705
		
2706 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...
2707
			$perm = call_user_func($this->access, 'write', $path, $this->options['accessControlData'], $this, $isDir);			
2708
			if ($perm !== null) {
2709
				return !!$perm;
2710
			}
2711
		}
2712
		
2713
		$testPath = $this->separator.$this->relpathCE($path);
2714
		
2715 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...
2716
			$attrs = $this->attributes[$i];
2717
			
2718
			if (isset($attrs['write']) && isset($attrs['pattern']) && preg_match($attrs['pattern'], $testPath)) {
2719
				$perm = $attrs['write'];
2720
			} 
2721
		}
2722
		
2723
		return $perm === null ? true : $perm;
2724
	}
2725
	
2726
	/**
2727
	 * Return true if file MIME type can save with check uploadOrder config.
2728
	 * 
2729
	 * @param string $mime
2730
	 * @return boolean
2731
	 */
2732
	protected function allowPutMime($mime) {
2733
		// logic based on http://httpd.apache.org/docs/2.2/mod/mod_authz_host.html#order
2734
		$allow  = $this->mimeAccepted($mime, $this->uploadAllow, null);
2735
		$deny   = $this->mimeAccepted($mime, $this->uploadDeny,  null);
2736
		$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...
2737
		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...
2738
			$res = false; // default is deny
2739
			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...
2740
				$res = true;
2741
			}// else (both match | no match | match only deny) { deny }
2742
		} else { // array('deny', 'allow'), default is to 'allow' - this is the default rule
2743
			$res = true; // default is allow
2744
			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...
2745
				$res = false;
2746
			} // else (both match | no match | match only allow) { allow }
2747
		}
2748
		return $res;
2749
	}
2750
	
2751
	/**
2752
	 * Return fileinfo 
2753
	 *
2754
	 * @param  string  $path  file cache
2755
	 * @return array
2756
	 * @author Dmitry (dio) Levashov
2757
	 **/
2758
	protected function stat($path) {
2759
		if ($path === false || is_null($path)) {
2760
			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...
2761
		}
2762
		$is_root = ($path === $this->root);
2763
		if ($is_root) {
2764
			$rootKey = md5($path);
2765
			if (!isset($this->sessionCache['rootstat'])) {
2766
				$this->sessionCache['rootstat'] = array();
2767
			}
2768
			// need $path as key for netmount/netunmount
2769
			if (isset($this->sessionCache['rootstat'][$rootKey])) {
2770
				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...
2771
					return $ret;
2772
				}
2773
			}
2774
		}
2775
		$ret = isset($this->cache[$path])
2776
			? $this->cache[$path]
2777
			: $this->updateCache($path, $this->convEncOut($this->_stat($this->convEncIn($path))));
2778
		if ($is_root) {
2779
			$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...
2780
		}
2781
		return $ret;
2782
	}
2783
	
2784
	/**
2785
	 * Put file stat in cache and return it
2786
	 *
2787
	 * @param  string  $path   file path
2788
	 * @param  array   $stat   file stat
2789
	 * @return array
2790
	 * @author Dmitry (dio) Levashov
2791
	 **/
2792
	protected function updateCache($path, $stat) {
2793
		if (empty($stat) || !is_array($stat)) {
2794
			return $this->cache[$path] = array();
2795
		}
2796
2797
		$stat['hash'] = $this->encode($path);
2798
2799
		$root = $path == $this->root;
2800
		$parent = '';
2801
		
2802
		if ($root) {
2803
			if ($this->rootName) {
2804
				$stat['name'] = $this->rootName;
2805
			}
2806
			if (! empty($this->options['icon'])) {
2807
				$stat['icon'] = $this->options['icon'];
2808
			}
2809
			if (! empty($this->options['rootCssClass'])) {
2810
				$stat['csscls'] = $this->options['rootCssClass'];
2811
			}
2812
		} else {
2813
			if (!isset($stat['name']) || $stat['name'] === '') {
2814
				$stat['name'] = $this->basenameCE($path);
2815
			}
2816
			if (empty($stat['phash'])) {
2817
				$parent = $this->dirnameCE($path);
2818
				$stat['phash'] = $this->encode($parent);
2819
			}
2820
		}
2821
		
2822
		// fix name if required
2823
		if ($this->options['utf8fix'] && $this->options['utf8patterns'] && $this->options['utf8replace']) {
2824
			$stat['name'] = json_decode(str_replace($this->options['utf8patterns'], $this->options['utf8replace'], json_encode($stat['name'])));
2825
		}
2826
		
2827
		
2828
		if (empty($stat['mime'])) {
2829
			$stat['mime'] = $this->mimetype($stat['name']);
2830
		}
2831
		
2832
		// @todo move dateformat to client
2833
		// $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...
2834
		// 	? $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...
2835
		// 	: 'unknown';
2836
			
2837
		if (!isset($stat['size'])) {
2838
			$stat['size'] = 'unknown';
2839
		}	
2840
2841
		if ($isDir = ($stat['mime'] === 'directory')) {
2842
			$stat['volumeid'] = $this->id;
2843
		}
2844
		
2845
		$stat['read']  = intval($this->attr($path, 'read', isset($stat['read']) ? !!$stat['read'] : null, $isDir));
2846
		$stat['write'] = intval($this->attr($path, 'write', isset($stat['write']) ? !!$stat['write'] : null, $isDir));
2847
		if ($root) {
2848
			$stat['locked'] = 1;
2849
		} else {
2850
			// lock when parent directory is not writable
2851
			if (!isset($stat['locked'])) {
2852
				$parent = $this->dirnameCE($path);
2853
				$pstat = isset($this->cache[$parent])? $this->cache[$parent] : array();
2854
				if (isset($pstat['write']) && !$pstat['write']) {
2855
					$stat['locked'] = true;
2856
				}
2857
			}
2858
			if ($this->attr($path, 'locked', isset($stat['locked']) ? !!$stat['locked'] : null, $isDir)) {
2859
				$stat['locked'] = 1;
2860
			} else {
2861
				unset($stat['locked']);
2862
			}
2863
		}
2864
2865
		if ($root) {
2866
			unset($stat['hidden']);
2867
		} elseif ($this->attr($path, 'hidden', isset($stat['hidden']) ? !!$stat['hidden'] : null, $isDir) 
2868
		|| !$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...
2869
			$stat['hidden'] = 1;
2870
		} else {
2871
			unset($stat['hidden']);
2872
		}
2873
		
2874
		if ($stat['read'] && empty($stat['hidden'])) {
2875
			
2876
			if ($isDir) {
2877
				// caching parent's subdirs
2878
				if ($parent) {
2879
					$this->subdirsCache[$parent] = true;
2880
				}
2881
				// for dir - check for subdirs
2882
				if ($this->options['checkSubfolders']) {
2883
					if (isset($stat['dirs'])) {
2884
						if ($stat['dirs']) {
2885
							$stat['dirs'] = 1;
2886
						} else {
2887
							unset($stat['dirs']);
2888
						}
2889
					} elseif (!empty($stat['alias']) && !empty($stat['target'])) {
2890
						$stat['dirs'] = isset($this->cache[$stat['target']])
2891
							? intval(isset($this->cache[$stat['target']]['dirs']))
2892
							: $this->subdirsCE($stat['target']);
2893
						
2894
					} elseif ($this->subdirsCE($path)) {
2895
						$stat['dirs'] = 1;
2896
					}
2897
				} else {
2898
					$stat['dirs'] = 1;
2899
				}
2900
			} else {
2901
				// for files - check for thumbnails
2902
				$p = isset($stat['target']) ? $stat['target'] : $path;
2903
				if ($this->tmbURL && !isset($stat['tmb']) && $this->canCreateTmb($p, $stat)) {
2904
					$tmb = $this->gettmb($p, $stat);
2905
					$stat['tmb'] = $tmb ? $tmb : 1;
2906
				}
2907
				
2908
			}
2909
			if (!isset($stat['url']) && $this->URL && $this->encoding) {
2910
				$_path = str_replace($this->separator, '/', substr($path, strlen($this->root) + 1));
2911
				$stat['url'] = rtrim($this->URL, '/') . '/' . str_replace('%2F', '/', rawurlencode($this->convEncIn($_path, true)));
2912
			}
2913
		} else {
2914
			if ($isDir) {
2915
				unset($stat['dirs']);
2916
			}
2917
		}
2918
		
2919
		if (!empty($stat['alias']) && !empty($stat['target'])) {
2920
			$stat['thash'] = $this->encode($stat['target']);
2921
			//$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...
2922
			unset($stat['target']);
2923
		}
2924
		
2925
		if (isset($this->options['netkey']) && $path === $this->root) {
2926
			$stat['netkey'] = $this->options['netkey'];
2927
		}
2928
		
2929
		return $this->cache[$path] = $stat;
2930
	}
2931
	
2932
	/**
2933
	 * Get stat for folder content and put in cache
2934
	 *
2935
	 * @param  string  $path
2936
	 * @return void
2937
	 * @author Dmitry (dio) Levashov
2938
	 **/
2939
	protected function cacheDir($path) {
2940
		$this->dirsCache[$path] = array();
2941
		$this->subdirsCache[$path] = false;
2942
2943
		foreach ($this->scandirCE($path) as $p) {
2944 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...
2945
				if ($stat['mime'] === 'directory') {
2946
					$this->subdirsCache[$path] = true;
2947
				}
2948
				$this->dirsCache[$path][] = $p;
2949
			}
2950
		}
2951
	}
2952
	
2953
	/**
2954
	 * Clean cache
2955
	 *
2956
	 * @return void
2957
	 * @author Dmitry (dio) Levashov
2958
	 **/
2959
	protected function clearcache() {
2960
		$this->cache = $this->dirsCache = array();
2961
	}
2962
	
2963
	/**
2964
	 * Return file mimetype
2965
	 *
2966
	 * @param  string  $path  file path
2967
	 * @return string
2968
	 * @author Dmitry (dio) Levashov
2969
	 **/
2970
	protected function mimetype($path, $name = '') {
2971
		$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...
2972
		
2973
		if ($name === '') {
2974
			$name = $path;
2975
		}
2976
		$ext = (false === $pos = strrpos($name, '.')) ? '' : substr($name, $pos + 1);
2977
		if ($this->mimeDetect == 'finfo') {
2978
			if ($type = @finfo_file($this->finfo, $path)) {
2979
				if ($ext && preg_match('~^application/(?:octet-stream|(?:x-)?zip)~', $type)) {
2980
					if (isset(elFinderVolumeDriver::$mimetypes[$ext])) $type = elFinderVolumeDriver::$mimetypes[$ext];
2981
				} else if ($ext === 'js' && preg_match('~^text/~', $type)) {
2982
					$type = 'text/javascript';
2983
				}
2984
			} else {
2985
				$type = 'unknown';
2986
			}
2987
		} elseif ($this->mimeDetect == 'mime_content_type') {
2988
			$type = mime_content_type($path);
2989
		} else {
2990
			$type = elFinderVolumeDriver::mimetypeInternalDetect($path);
2991
		}
2992
		
2993
		$type = explode(';', $type);
2994
		$type = trim($type[0]);
2995
2996 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...
2997
			// finfo return this mime for empty files
2998
			$type = 'text/plain';
2999
		} elseif ($type == 'application/x-zip') {
3000
			// http://elrte.org/redmine/issues/163
3001
			$type = 'application/zip';
3002
		}
3003
		
3004
		// mime type normalization
3005
		$_checkKey = strtolower($ext.':'.$type);
3006
		if (isset($this->options['mimeMap'][$_checkKey])) {
3007
			$type = $this->options['mimeMap'][$_checkKey];
3008
		}
3009
		
3010
		return $type == 'unknown' && $this->mimeDetect != 'internal'
3011
			? elFinderVolumeDriver::mimetypeInternalDetect($path)
3012
			: $type;
3013
		
3014
	}
3015
	
3016
	/**
3017
	 * Detect file mimetype using "internal" method
3018
	 *
3019
	 * @param  string  $path  file path
3020
	 * @return string
3021
	 * @author Dmitry (dio) Levashov
3022
	 **/
3023
	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...
3024
		$pinfo = pathinfo($path); 
3025
		$ext   = isset($pinfo['extension']) ? strtolower($pinfo['extension']) : '';
3026
		return isset(elFinderVolumeDriver::$mimetypes[$ext]) ? elFinderVolumeDriver::$mimetypes[$ext] : 'unknown';
3027
		
3028
	}
3029
	
3030
	/**
3031
	 * Return file/total directory size
3032
	 *
3033
	 * @param  string  $path  file path
3034
	 * @return int
3035
	 * @author Dmitry (dio) Levashov
3036
	 **/
3037
	protected function countSize($path) {
3038
		$stat = $this->stat($path);
3039
3040
		if (empty($stat) || !$stat['read'] || !empty($stat['hidden'])) {
3041
			return 'unknown';
3042
		}
3043
		
3044
		if ($stat['mime'] != 'directory') {
3045
			return $stat['size'];
3046
		}
3047
		
3048
		$subdirs = $this->options['checkSubfolders'];
3049
		$this->options['checkSubfolders'] = true;
3050
		$result = 0;
3051
		foreach ($this->getScandir($path) as $stat) {
3052
			$size = $stat['mime'] == 'directory' && $stat['read'] 
3053
				? $this->countSize($this->joinPathCE($path, $stat['name'])) 
3054
				: (isset($stat['size']) ? intval($stat['size']) : 0);
3055
			if ($size > 0) {
3056
				$result += $size;
3057
			}
3058
		}
3059
		$this->options['checkSubfolders'] = $subdirs;
3060
		return $result;
3061
	}
3062
	
3063
	/**
3064
	 * Return true if all mimes is directory or files
3065
	 *
3066
	 * @param  string  $mime1  mimetype
3067
	 * @param  string  $mime2  mimetype
3068
	 * @return bool
3069
	 * @author Dmitry (dio) Levashov
3070
	 **/
3071
	protected function isSameType($mime1, $mime2) {
3072
		return ($mime1 == 'directory' && $mime1 == $mime2) || ($mime1 != 'directory' && $mime2 != 'directory');
3073
	}
3074
	
3075
	/**
3076
	 * If file has required attr == $val - return file path,
3077
	 * If dir has child with has required attr == $val - return child path
3078
	 *
3079
	 * @param  string   $path  file path
3080
	 * @param  string   $attr  attribute name
3081
	 * @param  bool     $val   attribute value
3082
	 * @return string|false
3083
	 * @author Dmitry (dio) Levashov
3084
	 **/
3085
	protected function closestByAttr($path, $attr, $val) {
3086
		$stat = $this->stat($path);
3087
		
3088
		if (empty($stat)) {
3089
			return false;
3090
		}
3091
		
3092
		$v = isset($stat[$attr]) ? $stat[$attr] : false;
3093
		
3094
		if ($v == $val) {
3095
			return $path;
3096
		}
3097
3098
		return $stat['mime'] == 'directory'
3099
			? $this->childsByAttr($path, $attr, $val) 
3100
			: false;
3101
	}
3102
	
3103
	/**
3104
	 * Return first found children with required attr == $val
3105
	 *
3106
	 * @param  string   $path  file path
3107
	 * @param  string   $attr  attribute name
3108
	 * @param  bool     $val   attribute value
3109
	 * @return string|false
3110
	 * @author Dmitry (dio) Levashov
3111
	 **/
3112
	protected function childsByAttr($path, $attr, $val) {
3113
		foreach ($this->scandirCE($path) as $p) {
3114
			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...
3115
				return $_p;
3116
			}
3117
		}
3118
		return false;
3119
	}
3120
	
3121
	/*****************  get content *******************/
3122
	
3123
	/**
3124
	 * Return required dir's files info.
3125
	 * If onlyMimes is set - return only dirs and files of required mimes
3126
	 *
3127
	 * @param  string  $path  dir path
3128
	 * @return array
3129
	 * @author Dmitry (dio) Levashov
3130
	 **/
3131
	protected function getScandir($path) {
3132
		$files = array();
3133
		
3134
		!isset($this->dirsCache[$path]) && $this->cacheDir($path);
3135
3136
		foreach ($this->dirsCache[$path] as $p) {
3137
			if (($stat = $this->stat($p)) && empty($stat['hidden'])) {
3138
				$files[] = $stat;
3139
			}
3140
		}
3141
3142
		return $files;
3143
	}
3144
	
3145
	
3146
	/**
3147
	 * Return subdirs tree
3148
	 *
3149
	 * @param  string  $path  parent dir path
3150
	 * @param  int     $deep  tree deep
3151
	 * @return array
3152
	 * @author Dmitry (dio) Levashov
3153
	 **/
3154
	protected function gettree($path, $deep, $exclude='') {
3155
		$dirs = array();
3156
		
3157
		!isset($this->dirsCache[$path]) && $this->cacheDir($path);
3158
3159
		foreach ($this->dirsCache[$path] as $p) {
3160
			$stat = $this->stat($p);
3161
			
3162
			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...
3163
				$dirs[] = $stat;
3164
				if ($deep > 0 && !empty($stat['dirs'])) {
3165
					$dirs = array_merge($dirs, $this->gettree($p, $deep-1));
3166
				}
3167
			}
3168
		}
3169
3170
		return $dirs;
3171
	}	
3172
		
3173
	/**
3174
	 * Recursive files search
3175
	 *
3176
	 * @param  string  $path   dir path
3177
	 * @param  string  $q      search string
3178
	 * @param  array   $mimes
3179
	 * @return array
3180
	 * @author Dmitry (dio) Levashov
3181
	 **/
3182
	protected function doSearch($path, $q, $mimes) {
3183
		$result = array();
3184
3185
		foreach($this->scandirCE($path) as $p) {
3186
			@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...
3187
			$stat = $this->stat($p);
3188
3189
			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...
3190
				continue;
3191
			}
3192
3193
			if (!empty($stat['hidden']) || !$this->mimeAccepted($stat['mime'], $mimes)) {
3194
				continue;
3195
			}
3196
			
3197
			$name = $stat['name'];
3198
3199
			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...
3200
				$stat['path'] = $this->path($stat['hash']);
3201 View Code Duplication
				if ($this->URL && !isset($stat['url'])) {
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...
3202
					$path = str_replace($this->separator, '/', substr($p, strlen($this->root) + 1));
3203
					if ($this->encoding) {
3204
						$path = str_replace('%2F', '/', rawurlencode($this->convEncIn($path, true)));
3205
					}
3206
					$stat['url'] = $this->URL . $path;
3207
				}
3208
				
3209
				$result[] = $stat;
3210
			}
3211 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...
3212
				$result = array_merge($result, $this->doSearch($p, $q, $mimes));
3213
			}
3214
		}
3215
		
3216
		return $result;
3217
	}
3218
		
3219
	/**********************  manuipulations  ******************/
3220
		
3221
	/**
3222
	 * Copy file/recursive copy dir only in current volume.
3223
	 * Return new file path or false.
3224
	 *
3225
	 * @param  string  $src   source path
3226
	 * @param  string  $dst   destination dir path
3227
	 * @param  string  $name  new file name (optionaly)
3228
	 * @return string|false
3229
	 * @author Dmitry (dio) Levashov
3230
	 **/
3231
	protected function copy($src, $dst, $name) {
3232
		$srcStat = $this->stat($src);
3233
		$this->clearcache();
3234
		
3235
		if (!empty($srcStat['thash'])) {
3236
			$target = $this->decode($srcStat['thash']);
3237
			if (!$this->inpathCE($target, $this->root)) {
3238
				return $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']), elFinder::ERROR_MKOUTLINK);
3239
			}
3240
			$stat   = $this->stat($target);
3241
			$this->clearcache();
3242
			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...
3243
				? $this->joinPathCE($dst, $name)
3244
				: $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']));
3245
		} 
3246
		
3247
		if ($srcStat['mime'] == 'directory') {
3248
			$test = $this->stat($this->joinPathCE($dst, $name));
3249
			
3250 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...
3251
				return $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']));
3252
			}
3253
			
3254
			$dst = $this->joinPathCE($dst, $name);
3255
			
3256
			foreach ($this->getScandir($src) as $stat) {
3257
				if (empty($stat['hidden'])) {
3258
					$name = $stat['name'];
3259
					if (!$this->copy($this->joinPathCE($src, $name), $dst, $name)) {
3260
						$this->remove($dst, true); // fall back
3261
						return $this->setError($this->error, elFinder::ERROR_COPY, $this->_path($src));
3262
					}
3263
				}
3264
			}
3265
			$this->clearcache();
3266
			return $dst;
3267
		} 
3268
3269
		return $this->convEncOut($this->_copy($this->convEncIn($src), $this->convEncIn($dst), $this->convEncIn($name)))
3270
			? $this->joinPathCE($dst, $name) 
3271
			: $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']));
3272
	}
3273
3274
	/**
3275
	 * Move file
3276
	 * Return new file path or false.
3277
	 *
3278
	 * @param  string  $src   source path
3279
	 * @param  string  $dst   destination dir path
3280
	 * @param  string  $name  new file name 
3281
	 * @return string|false
3282
	 * @author Dmitry (dio) Levashov
3283
	 **/
3284
	protected function move($src, $dst, $name) {
3285
		$stat = $this->stat($src);
3286
		$stat['realpath'] = $src;
3287
		$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...
3288
		$this->clearcache();
3289
		
3290 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...
3291
			$this->removed[] = $stat;
3292
3293
			return $this->joinPathCE($dst, $name);
3294
		}
3295
3296
		return $this->setError(elFinder::ERROR_MOVE, $this->path($stat['hash']));
3297
	}
3298
3299
	/**
3300
	 * Copy file from another volume.
3301
	 * Return new file path or false.
3302
	 *
3303
	 * @param  Object  $volume       source volume
3304
	 * @param  string  $src          source file hash
3305
	 * @param  string  $destination  destination dir path
3306
	 * @param  string  $name         file name
3307
	 * @return string|false
3308
	 * @author Dmitry (dio) Levashov
3309
	 **/
3310
	protected function copyFrom($volume, $src, $destination, $name) {
3311
		
3312
		if (($source = $volume->file($src)) == false) {
3313
			return $this->setError(elFinder::ERROR_COPY, '#'.$src, $volume->error());
3314
		}
3315
		
3316
		$errpath = $volume->path($source['hash']);
3317
		
3318
		if (!$this->nameAccepted($source['name'])) {
3319
			return $this->setError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_INVALID_NAME);
3320
		}
3321
				
3322
		if (!$source['read']) {
3323
			return $this->setError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_PERM_DENIED);
3324
		}
3325
		
3326
		if ($source['mime'] == 'directory') {
3327
			$stat = $this->stat($this->joinPathCE($destination, $name));
3328
			$this->clearcache();
3329 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...
3330
				return $this->setError(elFinder::ERROR_COPY, $errpath);
3331
			}
3332
			
3333
			$path = $this->joinPathCE($destination, $name);
3334
			
3335
			foreach ($volume->scandir($src) as $entr) {
3336
				if (!$this->copyFrom($volume, $entr['hash'], $path, $entr['name'])) {
3337
					$this->remove($path, true); // fall back
3338
					return $this->setError($this->error, elFinder::ERROR_COPY, $errpath);
3339
				}
3340
			}
3341
			
3342
		} else {
3343
			// $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...
3344
			// $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...
3345
			if (($dim = $volume->dimensions($src))) {
3346
				$s = explode('x', $dim);
3347
				$source['width']  = $s[0];
3348
				$source['height'] = $s[1];
3349
			}
3350
			
3351
			if (($fp = $volume->open($src)) == false
3352
			|| ($path = $this->saveCE($fp, $destination, $name, $source)) == false) {
3353
				$fp && $volume->close($fp, $src);
3354
				return $this->setError(elFinder::ERROR_COPY, $errpath);
3355
			}
3356
			$volume->close($fp, $src);
3357
			
3358
			// MIME check
3359
			$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 3352 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...
3360
			$mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($stat['name']);
3361
			if ($stat['mime'] === $mimeByName) {
3362
				$mimeByName = '';
3363
			}
3364 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...
3365
				$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 3352 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...
3366
				return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME, $errpath);
3367
			}
3368
		}
3369
		
3370
		return $path;
3371
	}
3372
		
3373
	/**
3374
	 * Remove file/ recursive remove dir
3375
	 *
3376
	 * @param  string  $path   file path
3377
	 * @param  bool    $force  try to remove even if file locked
3378
	 * @return bool
3379
	 * @author Dmitry (dio) Levashov
3380
	 **/
3381
	protected function remove($path, $force = false) {
3382
		$stat = $this->stat($path);
3383
		
3384
		if (empty($stat)) {
3385
			return $this->setError(elFinder::ERROR_RM, $this->path($stat['hash']), elFinder::ERROR_FILE_NOT_FOUND);
3386
		}
3387
		
3388
		$stat['realpath'] = $path;
3389
		$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...
3390
		$this->clearcache();
3391
		
3392 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...
3393
			return $this->setError(elFinder::ERROR_LOCKED, $this->path($stat['hash']));
3394
		}
3395
		
3396
		if ($stat['mime'] == 'directory' && empty($stat['thash'])) {
3397
			$ret = $this->delTree($this->convEncIn($path));
3398
			$this->convEncOut();
3399
			if (!$ret) {
3400
				return $this->setError(elFinder::ERROR_RM, $this->path($stat['hash']));
3401
			}
3402 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...
3403
			if ($this->convEncOut(!$this->_unlink($this->convEncIn($path)))) {
3404
				return $this->setError(elFinder::ERROR_RM, $this->path($stat['hash']));
3405
			}
3406
		}
3407
3408
		$this->removed[] = $stat;
3409
		return true;
3410
	}
3411
	
3412
3413
	/************************* thumbnails **************************/
3414
		
3415
	/**
3416
	 * Return thumbnail file name for required file
3417
	 *
3418
	 * @param  array  $stat  file stat
3419
	 * @return string
3420
	 * @author Dmitry (dio) Levashov
3421
	 **/
3422
	protected function tmbname($stat) {
3423
		return $stat['hash'].$stat['ts'].'.png';
3424
	}
3425
	
3426
	/**
3427
	 * Return thumnbnail name if exists
3428
	 *
3429
	 * @param  string  $path file path
3430
	 * @param  array   $stat file stat
3431
	 * @return string|false
3432
	 * @author Dmitry (dio) Levashov
3433
	 **/
3434
	protected function gettmb($path, $stat) {
3435
		if ($this->tmbURL && $this->tmbPath) {
3436
			// file itself thumnbnail
3437
			if (strpos($path, $this->tmbPath) === 0) {
3438
				return basename($path);
3439
			}
3440
3441
			$name = $this->tmbname($stat);
3442
			if (file_exists($this->tmbPath.DIRECTORY_SEPARATOR.$name)) {
3443
				return $name;
3444
			}
3445
		}
3446
		return false;
3447
	}
3448
	
3449
	/**
3450
	 * Return true if thumnbnail for required file can be created
3451
	 *
3452
	 * @param  string  $path  thumnbnail path 
3453
	 * @param  array   $stat  file stat
3454
	 * @param  bool    $checkTmbPath
3455
	 * @return string|bool
3456
	 * @author Dmitry (dio) Levashov
3457
	 **/
3458
	protected function canCreateTmb($path, $stat, $checkTmbPath = true) {
3459
		return (!$checkTmbPath || $this->tmbPathWritable) 
3460
			&& (!$this->tmbPath || strpos($path, $this->tmbPath) === false) // do not create thumnbnail for thumnbnail
3461
			&& $this->imgLib 
3462
			&& strpos($stat['mime'], 'image') === 0 
3463
			&& ($this->imgLib == 'gd' ? $stat['mime'] == 'image/jpeg' || $stat['mime'] == 'image/png' || $stat['mime'] == 'image/gif' : true);
3464
	}
3465
	
3466
	/**
3467
	 * Return true if required file can be resized.
3468
	 * By default - the same as canCreateTmb
3469
	 *
3470
	 * @param  string  $path  thumnbnail path 
3471
	 * @param  array   $stat  file stat
3472
	 * @return string|bool
3473
	 * @author Dmitry (dio) Levashov
3474
	 **/
3475
	protected function canResize($path, $stat) {
3476
		return $this->canCreateTmb($path, $stat, false);
3477
	}
3478
	
3479
	/**
3480
	 * Create thumnbnail and return it's URL on success
3481
	 *
3482
	 * @param  string  $path  file path
3483
	 * @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...
3484
	 * @return string|false
3485
	 * @author Dmitry (dio) Levashov
3486
	 **/
3487
	protected function createTmb($path, $stat) {
3488
		if (!$stat || !$this->canCreateTmb($path, $stat)) {
3489
			return false;
3490
		}
3491
3492
		$name = $this->tmbname($stat);
3493
		$tmb  = $this->tmbPath.DIRECTORY_SEPARATOR.$name;
3494
3495
		// copy image into tmbPath so some drivers does not store files on local fs
3496
		if (($src = $this->fopenCE($path, 'rb')) == false) {
3497
			return false;
3498
		}
3499
3500
		if (($trg = fopen($tmb, 'wb')) == false) {
3501
			$this->fcloseCE($src, $path);
3502
			return false;
3503
		}
3504
3505
		while (!feof($src)) {
3506
			fwrite($trg, fread($src, 8192));
3507
		}
3508
3509
		$this->fcloseCE($src, $path);
3510
		fclose($trg);
3511
3512
		$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...
3513
		
3514
		$tmbSize = $this->tmbSize;
3515
		
3516
		if (($s = getimagesize($tmb)) == false) {
3517
			return false;
3518
		}
3519
3520
		/* If image smaller or equal thumbnail size - just fitting to thumbnail square */
3521
		if ($s[0] <= $tmbSize && $s[1]	<= $tmbSize) {
3522
			$result = $this->imgSquareFit($tmb, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png' );
3523
		} else {
3524
		
3525
			if ($this->options['tmbCrop']) {
3526
		
3527
				$result = $tmb;
3528
				/* Resize and crop if image bigger than thumbnail */
3529 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...
3530
					$result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, false, 'png');
3531
				}
3532
		
3533 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...
3534
					$x = $s[0] > $tmbSize ? intval(($s[0] - $tmbSize)/2) : 0;
3535
					$y = $s[1] > $tmbSize ? intval(($s[1] - $tmbSize)/2) : 0;
3536
					$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...
3537
				} else {
3538
					$result = false;
3539
				}
3540
		
3541
			} else {
3542
				$result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, true, 'png');
3543
			}
3544
		
3545
			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...
3546
				$result = $this->imgSquareFit($result, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png' );
3547
			}
3548
		}
3549
		
3550
		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...
3551
			unlink($tmb);
3552
			return false;
3553
		}
3554
		
3555
		return $name;
3556
	}
3557
3558
	/**
3559
	 * Resize image
3560
	 *
3561
	 * @param  string   $path               image file
3562
	 * @param  int      $width              new width
3563
	 * @param  int      $height             new height
3564
	 * @param  bool	    $keepProportions    crop image
3565
	 * @param  bool	    $resizeByBiggerSide resize image based on bigger side if true
3566
	 * @param  string   $destformat         image destination format
3567
	 * @return string|false
3568
	 * @author Dmitry (dio) Levashov
3569
	 * @author Alexey Sukhotin
3570
	 **/
3571
	protected function imgResize($path, $width, $height, $keepProportions = false, $resizeByBiggerSide = true, $destformat = null) {
3572
		if (($s = @getimagesize($path)) == false) {
3573
			return false;
3574
		}
3575
3576
		$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...
3577
		
3578
		list($size_w, $size_h) = array($width, $height);
3579
	
3580
		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...
3581
		
3582
			list($orig_w, $orig_h) = array($s[0], $s[1]);
3583
		
3584
			/* Resizing by biggest side */
3585
			if ($resizeByBiggerSide) {
3586 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...
3587
					$size_h = round($orig_h * $width / $orig_w);
3588
					$size_w = $width;
3589
				} else {
3590
					$size_w = round($orig_w * $height / $orig_h);
3591
					$size_h = $height;
3592
				}
3593 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...
3594
				if ($orig_w > $orig_h) {
3595
					$size_w = round($orig_w * $height / $orig_h);
3596
					$size_h = $height;
3597
				} else {
3598
					$size_h = round($orig_h * $width / $orig_w);
3599
					$size_w = $width;
3600
				}
3601
			}
3602
		}
3603
3604
		switch ($this->imgLib) {
3605
			case 'imagick':
3606
				
3607
				try {
3608
					$img = new imagick($path);
3609
				} catch (Exception $e) {
3610
					return false;
3611
				}
3612
3613
				// Imagick::FILTER_BOX faster than FILTER_LANCZOS so use for createTmb
3614
				// 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...
3615
				// 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...
3616
				$filter = ($destformat === 'png' /* createTmb */)? Imagick::FILTER_BOX : Imagick::FILTER_LANCZOS;
3617
				
3618
				$ani = ($img->getNumberImages() > 1);
3619
				if ($ani && is_null($destformat)) {
3620
					$img = $img->coalesceImages();
3621
					do {
3622
						$img->resizeImage($size_w, $size_h, $filter, 1);
3623
					} while ($img->nextImage());
3624
					$img = $img->optimizeImageLayers();
3625
					$result = $img->writeImages($path, true);
3626
				} else {
3627
					if ($ani) {
3628
						$img->setFirstIterator();
3629
					}
3630
					$img->resizeImage($size_w, $size_h, $filter, 1);
3631
					$result = $img->writeImage($path);
3632
				}
3633
				
3634
				$img->destroy();
3635
3636
				return $result ? $path : false;
3637
3638
				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...
3639
3640
			case 'gd':
3641
				$img = self::gdImageCreate($path,$s['mime']);
3642
3643
				if ($img &&  false != ($tmp = imagecreatetruecolor($size_w, $size_h))) {
3644
				
3645
					self::gdImageBackground($tmp,$this->options['tmbBgColor']);
3646
					
3647
					if (!imagecopyresampled($tmp, $img, 0, 0, 0, 0, $size_w, $size_h, $s[0], $s[1])) {
3648
						return false;
3649
					}
3650
		
3651
					$result = self::gdImage($tmp, $path, $destformat, $s['mime']);
3652
3653
					imagedestroy($img);
3654
					imagedestroy($tmp);
3655
3656
					return $result ? $path : false;
3657
3658
				}
3659
				break;
3660
		}
3661
		
3662
		return false;
3663
  	}
3664
  
3665
	/**
3666
	 * Crop image
3667
	 *
3668
	 * @param  string   $path               image file
3669
	 * @param  int      $width              crop width
3670
	 * @param  int      $height             crop height
3671
	 * @param  bool	    $x                  crop left offset
3672
	 * @param  bool	    $y                  crop top offset
3673
	 * @param  string   $destformat         image destination format
3674
	 * @return string|false
3675
	 * @author Dmitry (dio) Levashov
3676
	 * @author Alexey Sukhotin
3677
	 **/
3678
  	protected function imgCrop($path, $width, $height, $x, $y, $destformat = null) {
3679
		if (($s = @getimagesize($path)) == false) {
3680
			return false;
3681
		}
3682
3683
		$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...
3684
		
3685
		switch ($this->imgLib) {
3686
			case 'imagick':
3687
				
3688
				try {
3689
					$img = new imagick($path);
3690
				} catch (Exception $e) {
3691
					return false;
3692
				}
3693
				
3694
				$ani = ($img->getNumberImages() > 1);
3695
				if ($ani && is_null($destformat)) {
3696
					$img = $img->coalesceImages();
3697
					do {
3698
						$img->setImagePage($s[0], $s[1], 0, 0);
3699
						$img->cropImage($width, $height, $x, $y);
3700
						$img->setImagePage($width, $height, 0, 0);
3701
					} while ($img->nextImage());
3702
					$img = $img->optimizeImageLayers();
3703
					$result = $img->writeImages($path, true);
3704
				} else {
3705
					if ($ani) {
3706
						$img->setFirstIterator();
3707
					}
3708
					$img->setImagePage($s[0], $s[1], 0, 0);
3709
					$img->cropImage($width, $height, $x, $y);
3710
					$img->setImagePage($width, $height, 0, 0);
3711
					$result = $img->writeImage($path);
3712
				}
3713
				
3714
				$img->destroy();
3715
3716
				return $result ? $path : false;
3717
3718
				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...
3719
3720
			case 'gd':
3721
				$img = self::gdImageCreate($path,$s['mime']);
3722
3723
				if ($img &&  false != ($tmp = imagecreatetruecolor($width, $height))) {
3724
					
3725
					self::gdImageBackground($tmp,$this->options['tmbBgColor']);
3726
3727
					$size_w = $width;
3728
					$size_h = $height;
3729
3730
					if ($s[0] < $width || $s[1] < $height) {
3731
						$size_w = $s[0];
3732
						$size_h = $s[1];
3733
					}
3734
3735
					if (!imagecopy($tmp, $img, 0, 0, $x, $y, $size_w, $size_h)) {
3736
						return false;
3737
					}
3738
					
3739
					$result = self::gdImage($tmp, $path, $destformat, $s['mime']);
3740
3741
					imagedestroy($img);
3742
					imagedestroy($tmp);
3743
3744
					return $result ? $path : false;
3745
3746
				}
3747
				break;
3748
		}
3749
3750
		return false;
3751
	}
3752
3753
	/**
3754
	 * Put image to square
3755
	 *
3756
	 * @param  string   $path               image file
3757
	 * @param  int      $width              square width
3758
	 * @param  int      $height             square height
3759
	 * @param  int	    $align              reserved
3760
	 * @param  int 	    $valign             reserved
3761
	 * @param  string   $bgcolor            square background color in #rrggbb format
3762
	 * @param  string   $destformat         image destination format
3763
	 * @return string|false
3764
	 * @author Dmitry (dio) Levashov
3765
	 * @author Alexey Sukhotin
3766
	 **/
3767
	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...
3768
		if (($s = @getimagesize($path)) == false) {
3769
			return false;
3770
		}
3771
3772
		$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...
3773
3774
		/* Coordinates for image over square aligning */
3775
		$y = ceil(abs($height - $s[1]) / 2); 
3776
		$x = ceil(abs($width - $s[0]) / 2);
3777
3778
		switch ($this->imgLib) {
3779
			case 'imagick':
3780
				try {
3781
					$img = new imagick($path);
3782
				} catch (Exception $e) {
3783
					return false;
3784
				}
3785
				
3786
				$ani = ($img->getNumberImages() > 1);
3787
				if ($ani && is_null($destformat)) {
3788
					$img1 = new Imagick();
3789
					$img1->setFormat('gif');
3790
					$img = $img->coalesceImages();
3791
					do {
3792
						$gif = new Imagick();
3793
						$gif->newImage($width, $height, new ImagickPixel($bgcolor));
3794
						$gif->setImageColorspace($img->getImageColorspace());
3795
						$gif->setImageFormat('gif');
3796
						$gif->compositeImage( $img, imagick::COMPOSITE_OVER, $x, $y );
3797
						$gif->setImageDelay($img->getImageDelay());
3798
						$gif->setImageIterations($img->getImageIterations());
3799
						$img1->addImage($gif);
3800
						$gif->destroy();
3801
					} while ($img->nextImage());
3802
					$img1 = $img1->optimizeImageLayers();
3803
					$result = $img1->writeImages($path, true);
3804
				} else {
3805
					if ($ani) {
3806
						$img->setFirstIterator();
3807
					}
3808
					$img1 = new Imagick();
3809
					$img1->newImage($width, $height, new ImagickPixel($bgcolor));
3810
					$img1->setImageColorspace($img->getImageColorspace());
3811
					$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...
3812
					$img1->compositeImage( $img, imagick::COMPOSITE_OVER, $x, $y );
3813
					$result = $img1->writeImage($path);
3814
				}
3815
				
3816
				$img1->destroy();
3817
				$img->destroy();
3818
				return $result ? $path : false;
3819
3820
				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...
3821
3822
			case 'gd':
3823
				$img = self::gdImageCreate($path,$s['mime']);
3824
3825
				if ($img &&  false != ($tmp = imagecreatetruecolor($width, $height))) {
3826
3827
					self::gdImageBackground($tmp,$bgcolor);
3828
3829
					if (!imagecopy($tmp, $img, $x, $y, 0, 0, $s[0], $s[1])) {
3830
						return false;
3831
					}
3832
3833
					$result = self::gdImage($tmp, $path, $destformat, $s['mime']);
3834
3835
					imagedestroy($img);
3836
					imagedestroy($tmp);
3837
3838
					return $result ? $path : false;
3839
				}
3840
				break;
3841
		}
3842
3843
		return false;
3844
	}
3845
3846
	/**
3847
	 * Rotate image
3848
	 *
3849
	 * @param  string   $path               image file
3850
	 * @param  int      $degree             rotete degrees
3851
	 * @param  string   $bgcolor            square background color in #rrggbb format
3852
	 * @param  string   $destformat         image destination format
3853
	 * @return string|false
3854
	 * @author nao-pon
3855
	 * @author Troex Nevelin
3856
	 **/
3857
	protected function imgRotate($path, $degree, $bgcolor = '#ffffff', $destformat = null) {
3858
		if (($s = @getimagesize($path)) == false || $degree % 360 === 0) {
3859
			return false;
3860
		}
3861
3862
		$result = false;
3863
3864
		// try lossless rotate
3865
		if ($degree % 90 === 0 && in_array($s[2], array(IMAGETYPE_JPEG, IMAGETYPE_JPEG2000))) {
3866
			$count = ($degree / 90) % 4;
3867
			$exiftran = array(
3868
				1 => '-9',
3869
				2 => '-1',
3870
				3 => '-2'
3871
			);
3872
			$jpegtran = array(
3873
				1 => '90',
3874
				2 => '180',
3875
				3 => '270'
3876
			);
3877
			$quotedPath = escapeshellarg($path);
3878
			$cmds = array(
3879
				'exiftran -i '.$exiftran[$count].' '.$path,
3880
				'jpegtran -rotate '.$jpegtran[$count].' -copy all -outfile '.$quotedPath.' '.$quotedPath
3881
			);
3882
			foreach($cmds as $cmd) {
3883
				if ($this->procExec($cmd) === 0) {
3884
					$result = true;
3885
					break;
3886
				}
3887
			}
3888
			if ($result) {
3889
				return $path;
3890
			}
3891
		}
3892
3893
		switch ($this->imgLib) {
3894
			case 'imagick':
3895
				try {
3896
					$img = new imagick($path);
3897
				} catch (Exception $e) {
3898
					return false;
3899
				}
3900
3901
				if ($img->getNumberImages() > 1) {
3902
					$img = $img->coalesceImages();
3903
					do {
3904
						$img->rotateImage(new ImagickPixel($bgcolor), $degree);
3905
					} while ($img->nextImage());
3906
					$img = $img->optimizeImageLayers();
3907
					$result = $img->writeImages($path, true);
3908
				} else {
3909
					$img->rotateImage(new ImagickPixel($bgcolor), $degree);
3910
					$result = $img->writeImage($path);
3911
				}
3912
				$img->destroy();
3913
				return $result ? $path : false;
3914
3915
				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...
3916
3917
			case 'gd':
3918
				$img = self::gdImageCreate($path,$s['mime']);
3919
3920
				$degree = 360 - $degree;
3921
				list($r, $g, $b) = sscanf($bgcolor, "#%02x%02x%02x");
3922
				$bgcolor = imagecolorallocate($img, $r, $g, $b);
3923
				$tmp = imageRotate($img, $degree, (int)$bgcolor);
3924
3925
				$result = self::gdImage($tmp, $path, $destformat, $s['mime']);
3926
3927
				imageDestroy($img);
3928
				imageDestroy($tmp);
3929
3930
				return $result ? $path : false;
3931
3932
				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...
3933
		}
3934
3935
		return false;
3936
	}
3937
3938
	/**
3939
	 * Execute shell command
3940
	 *
3941
	 * @param  string  $command       command line
3942
	 * @param  array   $output        stdout strings
3943
	 * @param  array   $return_var    process exit code
3944
	 * @param  array   $error_output  stderr strings
3945
	 * @return int     exit code
3946
	 * @author Alexey Sukhotin
3947
	 **/
3948
	protected function procExec($command , array &$output = null, &$return_var = -1, array &$error_output = null) {
3949
3950
		$descriptorspec = array(
3951
			0 => array("pipe", "r"),  // stdin
3952
			1 => array("pipe", "w"),  // stdout
3953
			2 => array("pipe", "w")   // stderr
3954
		);
3955
3956
		$process = proc_open($command, $descriptorspec, $pipes, null, null);
3957
3958
		if (is_resource($process)) {
3959
3960
			fclose($pipes[0]);
3961
3962
			$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...
3963
			$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...
3964
3965
			$output = stream_get_contents($pipes[1]);
3966
			$error_output = stream_get_contents($pipes[2]);
3967
3968
			fclose($pipes[1]);
3969
			fclose($pipes[2]);
3970
			$return_var = proc_close($process);
3971
3972
3973
		}
3974
		
3975
		return $return_var;
3976
		
3977
	}
3978
3979
	/**
3980
	 * Remove thumbnail, also remove recursively if stat is directory
3981
	 *
3982
	 * @param  string  $stat  file stat
3983
	 * @return void
3984
	 * @author Dmitry (dio) Levashov
3985
	 * @author Naoki Sawada
3986
	 * @author Troex Nevelin
3987
	 **/
3988
	protected function rmTmb($stat) {
3989
		if ($stat['mime'] === 'directory') {
3990
			foreach ($this->scandirCE($this->decode($stat['hash'])) as $p) {
3991
				@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...
3992
				$name = $this->basenameCE($p);
3993
				$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...
3994
			}
3995
		} else if (!empty($stat['tmb']) && $stat['tmb'] != "1") {
3996
			$tmb = $this->tmbPath.DIRECTORY_SEPARATOR.$stat['tmb'];
3997
			file_exists($tmb) && @unlink($tmb);
3998
			clearstatcache();
3999
		}
4000
	}
4001
	
4002
	/**
4003
	 * Create an gd image according to the specified mime type
4004
	 *
4005
	 * @param string $path image file
4006
	 * @param string $mime
4007
	 * @return gd image resource identifier
4008
	 */
4009
	protected function gdImageCreate($path,$mime){
4010
		switch($mime){
4011
			case 'image/jpeg':
4012
			return imagecreatefromjpeg($path);
4013
4014
			case 'image/png':
4015
			return imagecreatefrompng($path);
4016
4017
			case 'image/gif':
4018
			return imagecreatefromgif($path);
4019
4020
			case 'image/xbm':
4021
			return imagecreatefromxbm($path);
4022
		}
4023
		return false;
4024
	}
4025
4026
	/**
4027
	 * Output gd image to file
4028
	 *
4029
	 * @param resource $image gd image resource
4030
	 * @param string $filename The path to save the file to.
4031
	 * @param string $destformat The Image type to use for $filename
4032
	 * @param string $mime The original image mime type
4033
	 */
4034
	protected function gdImage($image, $filename, $destformat, $mime ){
4035
4036
		if ($destformat == 'jpg' || ($destformat == null && $mime == 'image/jpeg')) {
4037
			return imagejpeg($image, $filename, 100);
4038
		}
4039
4040
		if ($destformat == 'gif' || ($destformat == null && $mime == 'image/gif')) {
4041
			return imagegif($image, $filename, 7);
4042
		}
4043
4044
		return imagepng($image, $filename, 7);
4045
	}
4046
4047
	/**
4048
	 * Assign the proper background to a gd image
4049
	 *
4050
	 * @param resource $image gd image resource
4051
	 * @param string $bgcolor background color in #rrggbb format
4052
	 */
4053
	protected function gdImageBackground($image, $bgcolor){
4054
4055
		if( $bgcolor == 'transparent' ){
4056
			imagesavealpha($image,true);
4057
			$bgcolor1 = imagecolorallocatealpha($image, 255, 255, 255, 127);
4058
4059
		}else{
4060
			list($r, $g, $b) = sscanf($bgcolor, "#%02x%02x%02x");
4061
			$bgcolor1 = imagecolorallocate($image, $r, $g, $b);
4062
		}
4063
4064
		imagefill($image, 0, 0, $bgcolor1);
4065
	}
4066
4067
	/*********************** misc *************************/
4068
	
4069
	/**
4070
	 * Return smart formatted date
4071
	 *
4072
	 * @param  int     $ts  file timestamp
4073
	 * @return string
4074
	 * @author Dmitry (dio) Levashov
4075
	 **/
4076
	// 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...
4077
	// 	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...
4078
	// 		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...
4079
	// 	}
4080
	// 	
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...
4081
	// 	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...
4082
	// 		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...
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
	// 	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...
4086
	// }
4087
4088
	/**
4089
	* Find position of first occurrence of string in a string with multibyte support
4090
	*
4091
	* @param  string  $haystack  The string being checked.
4092
	* @param  string  $needle    The string to find in haystack.
4093
	* @param  int     $offset    The search offset. If it is not specified, 0 is used.
4094
	* @return int|bool
4095
	* @author Alexey Sukhotin
4096
	**/
4097
	protected function stripos($haystack , $needle , $offset = 0) {
4098
		if (function_exists('mb_stripos')) {
4099
			return mb_stripos($haystack , $needle , $offset);
4100
		} else if (function_exists('mb_strtolower') && function_exists('mb_strpos')) {
4101
			return mb_strpos(mb_strtolower($haystack), mb_strtolower($needle), $offset);
4102
		} 
4103
		return stripos($haystack , $needle , $offset);
4104
	}
4105
4106
	/**
4107
	 * Get server side available archivers
4108
	 * 
4109
	 * @param bool $use_cache
4110
	 * @return array
4111
	 */
4112
	protected function getArchivers($use_cache = true) {
4113
4114
		$sessionKey = 'ARCHIVERS_CACHE';
4115
		if ($use_cache && isset($this->sessionCache[$sessionKey]) && is_array($this->sessionCache[$sessionKey])) {
4116
			return $this->sessionCache[$sessionKey];
4117
		}
4118
		
4119
		$arcs = array(
4120
			'create'  => array(),
4121
			'extract' => array()
4122
		);
4123
		
4124
		if (function_exists('proc_open')) {
4125
		
4126
			$this->procExec('tar --version', $o, $ctar);
4127
			
4128
			if ($ctar == 0) {
4129
				$arcs['create']['application/x-tar']  = array('cmd' => 'tar', 'argc' => '-cf', 'ext' => 'tar');
4130
				$arcs['extract']['application/x-tar'] = array('cmd' => 'tar', 'argc' => '-xf', 'ext' => 'tar');
4131
				unset($o);
4132
				$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...
4133 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...
4134
					$arcs['create']['application/x-gzip']  = array('cmd' => 'tar', 'argc' => '-czf', 'ext' => 'tgz');
4135
					$arcs['extract']['application/x-gzip'] = array('cmd' => 'tar', 'argc' => '-xzf', 'ext' => 'tgz');
4136
				}
4137
				unset($o);
4138
				$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...
4139 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...
4140
					$arcs['create']['application/x-bzip2']  = array('cmd' => 'tar', 'argc' => '-cjf', 'ext' => 'tbz');
4141
					$arcs['extract']['application/x-bzip2'] = array('cmd' => 'tar', 'argc' => '-xjf', 'ext' => 'tbz');
4142
				}
4143
				unset($o);
4144
				$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...
4145 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...
4146
					$arcs['create']['application/x-xz']  = array('cmd' => 'tar', 'argc' => '-cJf', 'ext' => 'xz');
4147
					$arcs['extract']['application/x-xz'] = array('cmd' => 'tar', 'argc' => '-xJf', 'ext' => 'xz');
4148
				}
4149
			}
4150
			unset($o);
4151
			$this->procExec('zip -v', $o, $c);
4152
			if ($c == 0) {
4153
				$arcs['create']['application/zip']  = array('cmd' => 'zip', 'argc' => '-r9', 'ext' => 'zip');
4154
			}
4155
			unset($o);
4156
			$this->procExec('unzip --help', $o, $c);
4157
			if ($c == 0) {
4158
				$arcs['extract']['application/zip'] = array('cmd' => 'unzip', 'argc' => '',  'ext' => 'zip');
4159
			}
4160
			unset($o);
4161
			$this->procExec('rar --version', $o, $c);
4162
			if ($c == 0 || $c == 7) {
4163
				$arcs['create']['application/x-rar']  = array('cmd' => 'rar', 'argc' => 'a -inul', 'ext' => 'rar');
4164
				$arcs['extract']['application/x-rar'] = array('cmd' => 'rar', 'argc' => 'x -y',    'ext' => 'rar');
4165
			} else {
4166
				unset($o);
4167
				$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...
4168
				if ($c==0 || $c == 7) {
4169
					$arcs['extract']['application/x-rar'] = array('cmd' => 'unrar', 'argc' => 'x -y', 'ext' => 'rar');
4170
				}
4171
			}
4172
			unset($o);
4173
			$this->procExec('7za --help', $o, $c);
4174
			if ($c == 0) {
4175
				$arcs['create']['application/x-7z-compressed']  = array('cmd' => '7za', 'argc' => 'a', 'ext' => '7z');
4176
				$arcs['extract']['application/x-7z-compressed'] = array('cmd' => '7za', 'argc' => 'x -y', 'ext' => '7z');
4177
				
4178
				if (empty($arcs['create']['application/zip'])) {
4179
					$arcs['create']['application/zip'] = array('cmd' => '7za', 'argc' => 'a -tzip', 'ext' => 'zip');
4180
				}
4181 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...
4182
					$arcs['extract']['application/zip'] = array('cmd' => '7za', 'argc' => 'x -tzip -y', 'ext' => 'zip');
4183
				}
4184
				if (empty($arcs['create']['application/x-tar'])) {
4185
					$arcs['create']['application/x-tar'] = array('cmd' => '7za', 'argc' => 'a -ttar', 'ext' => 'tar');
4186
				}
4187 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...
4188
					$arcs['extract']['application/x-tar'] = array('cmd' => '7za', 'argc' => 'x -ttar -y', 'ext' => 'tar');
4189
				}
4190
			} else if (substr(PHP_OS,0,3) === 'WIN') {
4191
				// check `7z` for Windows server.
4192
				unset($o);
4193
				$this->procExec('7z', $o, $c);
4194
				if ($c == 0) {
4195
					$arcs['create']['application/x-7z-compressed']  = array('cmd' => '7z', 'argc' => 'a', 'ext' => '7z');
4196
					$arcs['extract']['application/x-7z-compressed'] = array('cmd' => '7z', 'argc' => 'x -y', 'ext' => '7z');
4197
					
4198
					if (empty($arcs['create']['application/zip'])) {
4199
						$arcs['create']['application/zip'] = array('cmd' => '7z', 'argc' => 'a -tzip', 'ext' => 'zip');
4200
					}
4201
					if (empty($arcs['extract']['application/zip'])) {
4202
						$arcs['extract']['application/zip'] = array('cmd' => '7z', 'argc' => 'x -tzip -y', 'ext' => 'zip');
4203
					}
4204
					if (empty($arcs['create']['application/x-tar'])) {
4205
						$arcs['create']['application/x-tar'] = array('cmd' => '7z', 'argc' => 'a -ttar', 'ext' => 'tar');
4206
					}
4207
					if (empty($arcs['extract']['application/x-tar'])) {
4208
						$arcs['extract']['application/x-tar'] = array('cmd' => '7z', 'argc' => 'x -ttar -y', 'ext' => 'tar');
4209
					}
4210
				}
4211
			}
4212
		
4213
		}
4214
		
4215
		// Use PHP ZipArchive Class
4216
		if (class_exists('ZipArchive')) {
4217
			if (empty($arcs['create']['application/zip'])) {
4218
				$arcs['create']['application/zip']  = array('cmd' => 'phpfunction', 'argc' => 'self::zipArchiveZip', 'ext' => 'zip');
4219
			}
4220 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...
4221
				$arcs['extract']['application/zip'] = array('cmd' => 'phpfunction', 'argc' => 'self::zipArchiveUnzip', 'ext' => 'zip');
4222
			}
4223
		}
4224
		
4225
		$this->sessionCache[$sessionKey] = $arcs;
4226
		return $arcs;
4227
	}
4228
4229
	/**
4230
	 * Resolve relative / (Unix-like)absolute path
4231
	 * 
4232
	 * @param string $path  target path
4233
	 * @param string $base  base path
4234
	 * @return string
4235
	 */
4236
	protected function getFullPath($path, $base) {
4237
		$separator = $this->separator;
4238
		$systemroot = $this->systemRoot;
4239
4240
		$sepquoted = preg_quote($separator, '#');
4241
4242
		// normalize `/../`
4243
		$normreg = '#('.$sepquoted.')[^'.$sepquoted.']+'.$sepquoted.'\.\.'.$sepquoted.'#';
4244
		while(preg_match($normreg, $path)) {
4245
			$path = preg_replace($normreg, '$1', $path);
4246
		}
4247
		
4248
		// 'Here'
4249
		if ($path === '' || $path === '.' . $separator) return $base;
4250
		
4251
		// Absolute path
4252
		if ($path[0] === $separator || strpos($path, $systemroot) === 0) {
4253
			return $path;
4254
		}
4255
		
4256
		$preg_separator = '#' . $sepquoted . '#';
4257
		
4258
		// Relative path from 'Here'
4259
		if (substr($path, 0, 2) === '.' . $separator || $path[0] !== '.' || substr($path, 0, 3) !== '..' . $separator) {
4260
			$arrn = preg_split($preg_separator, $path, -1, PREG_SPLIT_NO_EMPTY);
4261
			if ($arrn[0] !== '.') {
4262
				array_unshift($arrn, '.');
4263
			}
4264
			$arrn[0] = $base;
4265
			return join($separator, $arrn);
4266
		}
4267
		
4268
		// Relative path from dirname()
4269
		if (substr($path, 0, 3) === '../') {
4270
			$arrn = preg_split($preg_separator, $path, -1, PREG_SPLIT_NO_EMPTY);
4271
			$arrp = preg_split($preg_separator, $base, -1, PREG_SPLIT_NO_EMPTY);
4272
		
4273
			while (! empty($arrn) && $arrn[0] === '..') {
4274
				array_shift($arrn);
4275
				array_pop($arrp);
4276
			}
4277
			$path = ! empty($arrp) ? $systemroot . join($separator, array_merge($arrp, $arrn)) :
4278
				(! empty($arrn) ? $systemroot . join($separator, $arrn) : $systemroot);
4279
		}
4280
		
4281
		return $path;
4282
	}
4283
4284
	/**
4285
	 * Remove directory recursive on local file system
4286
	 *
4287
	 * @param string $dir Target dirctory path
4288
	 * @return boolean
4289
	 * @author Naoki Sawada
4290
	 */
4291
	public function rmdirRecursive($dir) {
4292
		if (!is_link($dir) && is_dir($dir)) {
4293
			@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...
4294
			foreach (array_diff(scandir($dir), array('.', '..')) as $file) {
4295
				@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...
4296
				$path = $dir . DIRECTORY_SEPARATOR . $file;
4297
				if (!is_link($dir) && is_dir($path)) {
4298
					$this->rmdirRecursive($path);
4299
				} else {
4300
					@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...
4301
					@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...
4302
				}
4303
			}
4304
			return @rmdir($dir);
4305
		} else if (is_file($dir) || is_link($dir)) {
4306
			@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...
4307
			return @unlink($dir);
4308
		}
4309
		return false;
4310
	}
4311
4312
	/**
4313
	 * Create archive and return its path
4314
	 *
4315
	 * @param  string  $dir    target dir
4316
	 * @param  array   $files  files names list
4317
	 * @param  string  $name   archive name
4318
	 * @param  array   $arc    archiver options
4319
	 * @return string|bool
4320
	 * @author Dmitry (dio) Levashov, 
4321
	 * @author Alexey Sukhotin
4322
	 * @author Naoki Sawada
4323
	 **/
4324
	protected function makeArchive($dir, $files, $name, $arc) {
4325
		if ($arc['cmd'] === 'phpfunction') {
4326 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...
4327
				call_user_func_array($arc['argc'], array($dir, $files, $name));
4328
			}
4329
		} else {
4330
			$cwd = getcwd();
4331
			chdir($dir);
4332
			
4333
			$files = array_map('escapeshellarg', $files);
4334
			
4335
			$cmd = $arc['cmd'].' '.$arc['argc'].' '.escapeshellarg($name).' '.implode(' ', $files);
4336
			$this->procExec($cmd, $o, $c);
4337
			chdir($cwd);
4338
		}
4339
		$path = $dir.DIRECTORY_SEPARATOR.$name;
4340
		return file_exists($path) ? $path : false;
4341
	}
4342
	
4343
	/**
4344
	 * Unpack archive
4345
	 *
4346
	 * @param  string  $path   archive path
4347
	 * @param  array   $arc    archiver command and arguments (same as in $this->archivers)
4348
	 * @param  bool    $remove remove archive ( unlink($path) )
4349
	 * @return void
4350
	 * @author Dmitry (dio) Levashov
4351
	 * @author Alexey Sukhotin
4352
	 * @author Naoki Sawada
4353
	 **/
4354
	protected function unpackArchive($path, $arc, $remove = true) {
4355
		$dir = dirname($path);
4356
		if ($arc['cmd'] === 'phpfunction') {
4357 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...
4358
				call_user_func_array($arc['argc'], array($path, $dir));
4359
			}
4360
		} else {
4361
			$cwd = getcwd();
4362
			chdir($dir);
4363
			$cmd = $arc['cmd'].' '.$arc['argc'].' '.escapeshellarg(basename($path));
4364
			$this->procExec($cmd, $o, $c);
4365
			chdir($cwd);
4366
		}
4367
		$remove && unlink($path);
4368
	}
4369
	
4370
	/**
4371
	 * Create Zip archive using PHP class ZipArchive
4372
	 * 
4373
	 * @param  string        $dir      target dir
4374
	 * @param  array         $files    files names list
4375
	 * @param  string|object $zipPath  Zip archive name
4376
	 * @return void
4377
	 * @author Naoki Sawada
4378
	 */
4379
	protected static function zipArchiveZip($dir, $files, $zipPath) {
4380
		try {
4381
			if ($start = is_string($zipPath)) {
4382
				$zip = new ZipArchive();
4383
				if ($zip->open($dir . DIRECTORY_SEPARATOR . $zipPath, ZipArchive::CREATE) !== true) {
4384
					$zip = false;
4385
				}
4386
			} else {
4387
				$zip = $zipPath;
4388
			}
4389
			if ($zip) {
4390
				foreach($files as $file) {
4391
					$path = $dir . DIRECTORY_SEPARATOR . $file;
4392
					if (is_dir($path)) {
4393
						$zip->addEmptyDir($file);
4394
						$_files = array();
4395
						if ($handle = opendir($path)) {
4396
							while (false !== ($entry = readdir($handle))) {
4397
								if ($entry !== "." && $entry !== "..") {
4398
									$_files[] = $file . DIRECTORY_SEPARATOR . $entry;
4399
								}
4400
							}
4401
							closedir($handle);
4402
						}
4403
						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...
4404
							self::zipArchiveZip($dir, $_files, $zip);
4405
						}
4406
					} else {
4407
						$zip->addFile($path, $file);
4408
					}
4409
				}
4410
				$start && $zip->close();
4411
			}
4412
		} catch (Exception $e) {
4413
			return false;
4414
		}
4415
		return true;
4416
	}
4417
	
4418
	/**
4419
	 * Unpack Zip archive using PHP class ZipArchive
4420
	 * 
4421
	 * @param  string $zipPath  Zip archive name
4422
	 * @param  string $toDir    Extract to path
4423
	 * @return bool
4424
	 * @author Naoki Sawada
4425
	 */
4426
	protected static function zipArchiveUnzip($zipPath, $toDir) {
4427
		try {
4428
			$zip = new ZipArchive();
4429
			if ($zip->open($zipPath) === true) {
4430
				$zip->extractTo($toDir);
4431
				$zip->close();
4432
			}
4433
		} catch (Exception $e) {
4434
			return false;
4435
		}
4436
		return true;
4437
	}
4438
	
4439
	/**==================================* abstract methods *====================================**/
4440
	
4441
	/*********************** paths/urls *************************/
4442
	
4443
	/**
4444
	 * Return parent directory path
4445
	 *
4446
	 * @param  string  $path  file path
4447
	 * @return string
4448
	 * @author Dmitry (dio) Levashov
4449
	 **/
4450
	abstract protected function _dirname($path);
4451
4452
	/**
4453
	 * Return file name
4454
	 *
4455
	 * @param  string  $path  file path
4456
	 * @return string
4457
	 * @author Dmitry (dio) Levashov
4458
	 **/
4459
	abstract protected function _basename($path);
4460
4461
	/**
4462
	 * Join dir name and file name and return full path.
4463
	 * Some drivers (db) use int as path - so we give to concat path to driver itself
4464
	 *
4465
	 * @param  string  $dir   dir path
4466
	 * @param  string  $name  file name
4467
	 * @return string
4468
	 * @author Dmitry (dio) Levashov
4469
	 **/
4470
	abstract protected function _joinPath($dir, $name);
4471
4472
	/**
4473
	 * Return normalized path 
4474
	 *
4475
	 * @param  string  $path  file path
4476
	 * @return string
4477
	 * @author Dmitry (dio) Levashov
4478
	 **/
4479
	abstract protected function _normpath($path);
4480
4481
	/**
4482
	 * Return file path related to root dir
4483
	 *
4484
	 * @param  string  $path  file path
4485
	 * @return string
4486
	 * @author Dmitry (dio) Levashov
4487
	 **/
4488
	abstract protected function _relpath($path);
4489
	
4490
	/**
4491
	 * Convert path related to root dir into real path
4492
	 *
4493
	 * @param  string  $path  rel file path
4494
	 * @return string
4495
	 * @author Dmitry (dio) Levashov
4496
	 **/
4497
	abstract protected function _abspath($path);
4498
	
4499
	/**
4500
	 * Return fake path started from root dir.
4501
	 * Required to show path on client side.
4502
	 *
4503
	 * @param  string  $path  file path
4504
	 * @return string
4505
	 * @author Dmitry (dio) Levashov
4506
	 **/
4507
	abstract protected function _path($path);
4508
	
4509
	/**
4510
	 * Return true if $path is children of $parent
4511
	 *
4512
	 * @param  string  $path    path to check
4513
	 * @param  string  $parent  parent path
4514
	 * @return bool
4515
	 * @author Dmitry (dio) Levashov
4516
	 **/
4517
	abstract protected function _inpath($path, $parent);
4518
	
4519
	/**
4520
	 * Return stat for given path.
4521
	 * Stat contains following fields:
4522
	 * - (int)    size    file size in b. required
4523
	 * - (int)    ts      file modification time in unix time. required
4524
	 * - (string) mime    mimetype. required for folders, others - optionally
4525
	 * - (bool)   read    read permissions. required
4526
	 * - (bool)   write   write permissions. required
4527
	 * - (bool)   locked  is object locked. optionally
4528
	 * - (bool)   hidden  is object hidden. optionally
4529
	 * - (string) alias   for symlinks - link target path relative to root path. optionally
4530
	 * - (string) target  for symlinks - link target path. optionally
4531
	 *
4532
	 * If file does not exists - returns empty array or false.
4533
	 *
4534
	 * @param  string  $path    file path 
4535
	 * @return array|false
4536
	 * @author Dmitry (dio) Levashov
4537
	 **/
4538
	abstract protected function _stat($path);
4539
	
4540
4541
	/***************** file stat ********************/
4542
4543
		
4544
	/**
4545
	 * Return true if path is dir and has at least one childs directory
4546
	 *
4547
	 * @param  string  $path  dir path
4548
	 * @return bool
4549
	 * @author Dmitry (dio) Levashov
4550
	 **/
4551
	abstract protected function _subdirs($path);
4552
	
4553
	/**
4554
	 * Return object width and height
4555
	 * Ususaly used for images, but can be realize for video etc...
4556
	 *
4557
	 * @param  string  $path  file path
4558
	 * @param  string  $mime  file mime type
4559
	 * @return string
4560
	 * @author Dmitry (dio) Levashov
4561
	 **/
4562
	abstract protected function _dimensions($path, $mime);
4563
	
4564
	/******************** file/dir content *********************/
4565
4566
	/**
4567
	 * Return files list in directory
4568
	 *
4569
	 * @param  string  $path  dir path
4570
	 * @return array
4571
	 * @author Dmitry (dio) Levashov
4572
	 **/
4573
	abstract protected function _scandir($path);
4574
	
4575
	/**
4576
	 * Open file and return file pointer
4577
	 *
4578
	 * @param  string  $path  file path
4579
	 * @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...
4580
	 * @return resource|false
4581
	 * @author Dmitry (dio) Levashov
4582
	 **/
4583
	abstract protected function _fopen($path, $mode="rb");
4584
	
4585
	/**
4586
	 * Close opened file
4587
	 * 
4588
	 * @param  resource  $fp    file pointer
4589
	 * @param  string    $path  file path
4590
	 * @return bool
4591
	 * @author Dmitry (dio) Levashov
4592
	 **/
4593
	abstract protected function _fclose($fp, $path='');
4594
	
4595
	/********************  file/dir manipulations *************************/
4596
	
4597
	/**
4598
	 * Create dir and return created dir path or false on failed
4599
	 *
4600
	 * @param  string  $path  parent dir path
4601
	 * @param string  $name  new directory name
4602
	 * @return string|bool
4603
	 * @author Dmitry (dio) Levashov
4604
	 **/
4605
	abstract protected function _mkdir($path, $name);
4606
	
4607
	/**
4608
	 * Create file and return it's path or false on failed
4609
	 *
4610
	 * @param  string  $path  parent dir path
4611
	 * @param string  $name  new file name
4612
	 * @return string|bool
4613
	 * @author Dmitry (dio) Levashov
4614
	 **/
4615
	abstract protected function _mkfile($path, $name);
4616
	
4617
	/**
4618
	 * Create symlink
4619
	 *
4620
	 * @param  string  $source     file to link to
4621
	 * @param  string  $targetDir  folder to create link in
4622
	 * @param  string  $name       symlink name
4623
	 * @return bool
4624
	 * @author Dmitry (dio) Levashov
4625
	 **/
4626
	abstract protected function _symlink($source, $targetDir, $name);
4627
	
4628
	/**
4629
	 * Copy file into another file (only inside one volume)
4630
	 *
4631
	 * @param  string  $source  source file path
4632
	 * @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...
4633
	 * @param  string  $name    file name
4634
	 * @return bool
4635
	 * @author Dmitry (dio) Levashov
4636
	 **/
4637
	abstract protected function _copy($source, $targetDir, $name);
4638
	
4639
	/**
4640
	 * Move file into another parent dir.
4641
	 * Return new file path or false.
4642
	 *
4643
	 * @param  string  $source  source file path
4644
	 * @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...
4645
	 * @param  string  $name    file name
4646
	 * @return string|bool
4647
	 * @author Dmitry (dio) Levashov
4648
	 **/
4649
	abstract protected function _move($source, $targetDir, $name);
4650
	
4651
	/**
4652
	 * Remove file
4653
	 *
4654
	 * @param  string  $path  file path
4655
	 * @return bool
4656
	 * @author Dmitry (dio) Levashov
4657
	 **/
4658
	abstract protected function _unlink($path);
4659
4660
	/**
4661
	 * Remove dir
4662
	 *
4663
	 * @param  string  $path  dir path
4664
	 * @return bool
4665
	 * @author Dmitry (dio) Levashov
4666
	 **/
4667
	abstract protected function _rmdir($path);
4668
4669
	/**
4670
	 * Create new file and write into it from file pointer.
4671
	 * Return new file path or false on error.
4672
	 *
4673
	 * @param  resource  $fp   file pointer
4674
	 * @param  string    $dir  target dir path
4675
	 * @param  string    $name file name
4676
	 * @param  array     $stat file stat (required by some virtual fs)
4677
	 * @return bool|string
4678
	 * @author Dmitry (dio) Levashov
4679
	 **/
4680
	abstract protected function _save($fp, $dir, $name, $stat);
4681
	
4682
	/**
4683
	 * Get file contents
4684
	 *
4685
	 * @param  string  $path  file path
4686
	 * @return string|false
4687
	 * @author Dmitry (dio) Levashov
4688
	 **/
4689
	abstract protected function _getContents($path);
4690
	
4691
	/**
4692
	 * Write a string to a file
4693
	 *
4694
	 * @param  string  $path     file path
4695
	 * @param  string  $content  new file content
4696
	 * @return bool
4697
	 * @author Dmitry (dio) Levashov
4698
	 **/
4699
	abstract protected function _filePutContents($path, $content);
4700
4701
	/**
4702
	 * Extract files from archive
4703
	 *
4704
	 * @param  string  $path file path
4705
	 * @param  array   $arc  archiver options
4706
	 * @return bool
4707
	 * @author Dmitry (dio) Levashov, 
4708
	 * @author Alexey Sukhotin
4709
	 **/
4710
	abstract protected function _extract($path, $arc);
4711
4712
	/**
4713
	 * Create archive and return its path
4714
	 *
4715
	 * @param  string  $dir    target dir
4716
	 * @param  array   $files  files names list
4717
	 * @param  string  $name   archive name
4718
	 * @param  array   $arc    archiver options
4719
	 * @return string|bool
4720
	 * @author Dmitry (dio) Levashov, 
4721
	 * @author Alexey Sukhotin
4722
	 **/
4723
	abstract protected function _archive($dir, $files, $name, $arc);
4724
4725
	/**
4726
	 * Detect available archivers
4727
	 *
4728
	 * @return void
4729
	 * @author Dmitry (dio) Levashov, 
4730
	 * @author Alexey Sukhotin
4731
	 **/
4732
	abstract protected function _checkArchivers();
4733
4734
	/**
4735
	 * Change file mode (chmod)
4736
	 *
4737
	 * @param  string  $path  file path
4738
	 * @param  string  $mode  octal string such as '0755'
4739
	 * @return bool
4740
	 * @author David Bartle,
4741
	 **/
4742
	abstract protected function _chmod($path, $mode);
4743
4744
	
4745
} // END class
4746