Completed
Push — 2.x ( 70cab8...47be3c )
by Naoki
03:26
created

elFinderVolumeDriver::getHash()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

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