Completed
Push — 2.x ( e18f53...0476fa )
by Naoki
03:39
created

elFinderVolumeDriver::_mkdir()

Size

Total Lines 1

Duplication

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

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

namespace YourVendor;

class YourClass { }

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

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

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

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

Loading history...
2685
			$attrs = $this->attributes[$i];
2686
			
2687
			if (isset($attrs['write']) && isset($attrs['pattern']) && preg_match($attrs['pattern'], $testPath)) {
2688
				$perm = $attrs['write'];
2689
			} 
2690
		}
2691
		
2692
		return $perm === null ? true : $perm;
2693
	}
2694
	
2695
	/**
2696
	 * Return true if file MIME type can save with check uploadOrder config.
2697
	 * 
2698
	 * @param string $mime
2699
	 * @return boolean
2700
	 */
2701
	protected function allowPutMime($mime) {
2702
		// logic based on http://httpd.apache.org/docs/2.2/mod/mod_authz_host.html#order
2703
		$allow  = $this->mimeAccepted($mime, $this->uploadAllow, null);
2704
		$deny   = $this->mimeAccepted($mime, $this->uploadDeny,  null);
2705
		$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...
2706
		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...
2707
			$res = false; // default is deny
2708
			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...
2709
				$res = true;
2710
			}// else (both match | no match | match only deny) { deny }
2711
		} else { // array('deny', 'allow'), default is to 'allow' - this is the default rule
2712
			$res = true; // default is allow
2713
			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...
2714
				$res = false;
2715
			} // else (both match | no match | match only allow) { allow }
2716
		}
2717
		return $res;
2718
	}
2719
	
2720
	/**
2721
	 * Return fileinfo 
2722
	 *
2723
	 * @param  string  $path  file cache
2724
	 * @return array
2725
	 * @author Dmitry (dio) Levashov
2726
	 **/
2727
	protected function stat($path) {
2728
		if ($path === false || is_null($path)) {
1 ignored issue
show
Coding Style introduced by
As per coding-style, please use === null instead of is_null.
Loading history...
2729
			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...
2730
		}
2731
		$is_root = ($path === $this->root);
2732
		if ($is_root) {
2733
			$rootKey = md5($path);
2734
			if (!isset($this->sessionCache['rootstat'])) {
2735
				$this->sessionCache['rootstat'] = array();
2736
			}
2737
			// need $path as key for netmount/netunmount
2738
			if (isset($this->sessionCache['rootstat'][$rootKey])) {
2739
				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...
2740
					return $ret;
2741
				}
2742
			}
2743
		}
2744
		$ret = isset($this->cache[$path])
2745
			? $this->cache[$path]
2746
			: $this->updateCache($path, $this->convEncOut($this->_stat($this->convEncIn($path))));
2747
		if ($is_root) {
2748
			$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...
2749
		}
2750
		return $ret;
2751
	}
2752
	
2753
	/**
2754
	 * Put file stat in cache and return it
2755
	 *
2756
	 * @param  string  $path   file path
2757
	 * @param  array   $stat   file stat
2758
	 * @return array
2759
	 * @author Dmitry (dio) Levashov
2760
	 **/
2761
	protected function updateCache($path, $stat) {
2762
		if (empty($stat) || !is_array($stat)) {
2763
			return $this->cache[$path] = array();
2764
		}
2765
2766
		$stat['hash'] = $this->encode($path);
2767
2768
		$root = $path == $this->root;
2769
		$parent = '';
2770
		
2771
		if ($root) {
2772
			if ($this->rootName) {
2773
				$stat['name'] = $this->rootName;
2774
			}
2775
			if (! empty($this->options['icon'])) {
2776
				$stat['icon'] = $this->options['icon'];
2777
			}
2778
			if (! empty($this->options['rootCssClass'])) {
2779
				$stat['csscls'] = $this->options['rootCssClass'];
2780
			}
2781
		} else {
2782
			if (!isset($stat['name']) || $stat['name'] === '') {
2783
				$stat['name'] = $this->basenameCE($path);
2784
			}
2785
			if (empty($stat['phash'])) {
2786
				$parent = $this->dirnameCE($path);
2787
				$stat['phash'] = $this->encode($parent);
2788
			}
2789
		}
2790
		
2791
		// fix name if required
2792
		if ($this->options['utf8fix'] && $this->options['utf8patterns'] && $this->options['utf8replace']) {
2793
			$stat['name'] = json_decode(str_replace($this->options['utf8patterns'], $this->options['utf8replace'], json_encode($stat['name'])));
2794
		}
2795
		
2796
		
2797
		if (empty($stat['mime'])) {
2798
			$stat['mime'] = $this->mimetype($stat['name']);
2799
		}
2800
		
2801
		// @todo move dateformat to client
2802
		// $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...
2803
		// 	? $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...
2804
		// 	: 'unknown';
2805
			
2806
		if (!isset($stat['size'])) {
2807
			$stat['size'] = 'unknown';
2808
		}	
2809
2810
		if ($isDir = ($stat['mime'] === 'directory')) {
2811
			$stat['volumeid'] = $this->id;
2812
		}
2813
		
2814
		$stat['read']  = intval($this->attr($path, 'read', isset($stat['read']) ? !!$stat['read'] : null, $isDir));
2815
		$stat['write'] = intval($this->attr($path, 'write', isset($stat['write']) ? !!$stat['write'] : null, $isDir));
2816
		if ($root) {
2817
			$stat['locked'] = 1;
2818
		} else {
2819
			// lock when parent directory is not writable
2820
			if (!isset($stat['locked'])) {
2821
				$parent = $this->dirnameCE($path);
2822
				$pstat = isset($this->cache[$parent])? $this->cache[$parent] : array();
2823
				if (isset($pstat['write']) && !$pstat['write']) {
2824
					$stat['locked'] = true;
2825
				}
2826
			}
2827
			if ($this->attr($path, 'locked', isset($stat['locked']) ? !!$stat['locked'] : null, $isDir)) {
2828
				$stat['locked'] = 1;
2829
			} else {
2830
				unset($stat['locked']);
2831
			}
2832
		}
2833
2834
		if ($root) {
2835
			unset($stat['hidden']);
2836
		} elseif ($this->attr($path, 'hidden', isset($stat['hidden']) ? !!$stat['hidden'] : null, $isDir) 
2837
		|| !$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...
2838
			$stat['hidden'] = 1;
2839
		} else {
2840
			unset($stat['hidden']);
2841
		}
2842
		
2843
		if ($stat['read'] && empty($stat['hidden'])) {
2844
			
2845
			if ($isDir) {
2846
				// caching parent's subdirs
2847
				if ($parent) {
2848
					$this->subdirsCache[$parent] = true;
2849
				}
2850
				// for dir - check for subdirs
2851
				if ($this->options['checkSubfolders']) {
2852
					if (isset($stat['dirs'])) {
2853
						if ($stat['dirs']) {
2854
							$stat['dirs'] = 1;
2855
						} else {
2856
							unset($stat['dirs']);
2857
						}
2858
					} elseif (!empty($stat['alias']) && !empty($stat['target'])) {
2859
						$stat['dirs'] = isset($this->cache[$stat['target']])
2860
							? intval(isset($this->cache[$stat['target']]['dirs']))
2861
							: $this->subdirsCE($stat['target']);
2862
						
2863
					} elseif ($this->subdirsCE($path)) {
2864
						$stat['dirs'] = 1;
2865
					}
2866
				} else {
2867
					$stat['dirs'] = 1;
2868
				}
2869
			} else {
2870
				// for files - check for thumbnails
2871
				$p = isset($stat['target']) ? $stat['target'] : $path;
2872
				if ($this->tmbURL && !isset($stat['tmb']) && $this->canCreateTmb($p, $stat)) {
2873
					$tmb = $this->gettmb($p, $stat);
2874
					$stat['tmb'] = $tmb ? $tmb : 1;
2875
				}
2876
				
2877
			}
2878
			if (!isset($stat['url']) && $this->URL && $this->encoding) {
2879
				$_path = str_replace($this->separator, '/', substr($path, strlen($this->root) + 1));
2880
				$stat['url'] = rtrim($this->URL, '/') . '/' . str_replace('%2F', '/', rawurlencode($this->convEncIn($_path, true)));
2881
			}
2882
		} else {
2883
			if ($isDir) {
2884
				unset($stat['dirs']);
2885
			}
2886
		}
2887
		
2888
		if (!empty($stat['alias']) && !empty($stat['target'])) {
2889
			$stat['thash'] = $this->encode($stat['target']);
2890
			//$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...
2891
			unset($stat['target']);
2892
		}
2893
		
2894
		if (isset($this->options['netkey']) && $path === $this->root) {
2895
			$stat['netkey'] = $this->options['netkey'];
2896
		}
2897
		
2898
		return $this->cache[$path] = $stat;
2899
	}
2900
	
2901
	/**
2902
	 * Get stat for folder content and put in cache
2903
	 *
2904
	 * @param  string  $path
2905
	 * @return void
2906
	 * @author Dmitry (dio) Levashov
2907
	 **/
2908
	protected function cacheDir($path) {
2909
		$this->dirsCache[$path] = array();
2910
		$this->subdirsCache[$path] = false;
2911
2912
		foreach ($this->scandirCE($path) as $p) {
2913 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...
2914
				if ($stat['mime'] === 'directory') {
2915
					$this->subdirsCache[$path] = true;
2916
				}
2917
				$this->dirsCache[$path][] = $p;
2918
			}
2919
		}
2920
	}
2921
	
2922
	/**
2923
	 * Clean cache
2924
	 *
2925
	 * @return void
2926
	 * @author Dmitry (dio) Levashov
2927
	 **/
2928
	protected function clearcache() {
2929
		$this->cache = $this->dirsCache = array();
2930
	}
2931
	
2932
	/**
2933
	 * Return file mimetype
2934
	 *
2935
	 * @param  string  $path  file path
2936
	 * @return string
2937
	 * @author Dmitry (dio) Levashov
2938
	 **/
2939
	protected function mimetype($path, $name = '') {
2940
		$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...
2941
		
2942
		if ($name === '') {
2943
			$name = $path;
2944
		}
2945
		$ext = (false === $pos = strrpos($name, '.')) ? '' : substr($name, $pos + 1);
2946
		if ($this->mimeDetect == 'finfo') {
2947
			if ($type = @finfo_file($this->finfo, $path)) {
2948
				if ($ext && preg_match('~^application/(?:octet-stream|(?:x-)?zip)~', $type)) {
2949
					if (isset(elFinderVolumeDriver::$mimetypes[$ext])) $type = elFinderVolumeDriver::$mimetypes[$ext];
2950
				} else if ($ext === 'js' && preg_match('~^text/~', $type)) {
2951
					$type = 'text/javascript';
2952
				}
2953
			} else {
2954
				$type = 'unknown';
2955
			}
2956
		} elseif ($this->mimeDetect == 'mime_content_type') {
2957
			$type = mime_content_type($path);
2958
		} else {
2959
			$type = elFinderVolumeDriver::mimetypeInternalDetect($path);
2960
		}
2961
		
2962
		$type = explode(';', $type);
2963
		$type = trim($type[0]);
2964
2965 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...
2966
			// finfo return this mime for empty files
2967
			$type = 'text/plain';
2968
		} elseif ($type == 'application/x-zip') {
2969
			// http://elrte.org/redmine/issues/163
2970
			$type = 'application/zip';
2971
		}
2972
		
2973
		// mime type normalization
2974
		$_checkKey = strtolower($ext.':'.$type);
2975
		if (isset($this->options['mimeMap'][$_checkKey])) {
2976
			$type = $this->options['mimeMap'][$_checkKey];
2977
		}
2978
		
2979
		return $type == 'unknown' && $this->mimeDetect != 'internal'
2980
			? elFinderVolumeDriver::mimetypeInternalDetect($path)
2981
			: $type;
2982
		
2983
	}
2984
	
2985
	/**
2986
	 * Detect file mimetype using "internal" method
2987
	 *
2988
	 * @param  string  $path  file path
2989
	 * @return string
2990
	 * @author Dmitry (dio) Levashov
2991
	 **/
2992
	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...
2993
		$pinfo = pathinfo($path); 
2994
		$ext   = isset($pinfo['extension']) ? strtolower($pinfo['extension']) : '';
2995
		return isset(elFinderVolumeDriver::$mimetypes[$ext]) ? elFinderVolumeDriver::$mimetypes[$ext] : 'unknown';
2996
		
2997
	}
2998
	
2999
	/**
3000
	 * Return file/total directory size
3001
	 *
3002
	 * @param  string  $path  file path
3003
	 * @return int
3004
	 * @author Dmitry (dio) Levashov
3005
	 **/
3006
	protected function countSize($path) {
3007
		$stat = $this->stat($path);
3008
3009
		if (empty($stat) || !$stat['read'] || !empty($stat['hidden'])) {
3010
			return 'unknown';
3011
		}
3012
		
3013
		if ($stat['mime'] != 'directory') {
3014
			return $stat['size'];
3015
		}
3016
		
3017
		$subdirs = $this->options['checkSubfolders'];
3018
		$this->options['checkSubfolders'] = true;
3019
		$result = 0;
3020
		foreach ($this->getScandir($path) as $stat) {
3021
			$size = $stat['mime'] == 'directory' && $stat['read'] 
3022
				? $this->countSize($this->joinPathCE($path, $stat['name'])) 
3023
				: (isset($stat['size']) ? intval($stat['size']) : 0);
3024
			if ($size > 0) {
3025
				$result += $size;
3026
			}
3027
		}
3028
		$this->options['checkSubfolders'] = $subdirs;
3029
		return $result;
3030
	}
3031
	
3032
	/**
3033
	 * Return true if all mimes is directory or files
3034
	 *
3035
	 * @param  string  $mime1  mimetype
3036
	 * @param  string  $mime2  mimetype
3037
	 * @return bool
3038
	 * @author Dmitry (dio) Levashov
3039
	 **/
3040
	protected function isSameType($mime1, $mime2) {
3041
		return ($mime1 == 'directory' && $mime1 == $mime2) || ($mime1 != 'directory' && $mime2 != 'directory');
3042
	}
3043
	
3044
	/**
3045
	 * If file has required attr == $val - return file path,
3046
	 * If dir has child with has required attr == $val - return child path
3047
	 *
3048
	 * @param  string   $path  file path
3049
	 * @param  string   $attr  attribute name
3050
	 * @param  bool     $val   attribute value
3051
	 * @return string|false
3052
	 * @author Dmitry (dio) Levashov
3053
	 **/
3054
	protected function closestByAttr($path, $attr, $val) {
3055
		$stat = $this->stat($path);
3056
		
3057
		if (empty($stat)) {
3058
			return false;
3059
		}
3060
		
3061
		$v = isset($stat[$attr]) ? $stat[$attr] : false;
3062
		
3063
		if ($v == $val) {
3064
			return $path;
3065
		}
3066
3067
		return $stat['mime'] == 'directory'
3068
			? $this->childsByAttr($path, $attr, $val) 
3069
			: false;
3070
	}
3071
	
3072
	/**
3073
	 * Return first found children with required attr == $val
3074
	 *
3075
	 * @param  string   $path  file path
3076
	 * @param  string   $attr  attribute name
3077
	 * @param  bool     $val   attribute value
3078
	 * @return string|false
3079
	 * @author Dmitry (dio) Levashov
3080
	 **/
3081
	protected function childsByAttr($path, $attr, $val) {
3082
		foreach ($this->scandirCE($path) as $p) {
3083
			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...
3084
				return $_p;
3085
			}
3086
		}
3087
		return false;
3088
	}
3089
	
3090
	/*****************  get content *******************/
3091
	
3092
	/**
3093
	 * Return required dir's files info.
3094
	 * If onlyMimes is set - return only dirs and files of required mimes
3095
	 *
3096
	 * @param  string  $path  dir path
3097
	 * @return array
3098
	 * @author Dmitry (dio) Levashov
3099
	 **/
3100
	protected function getScandir($path) {
3101
		$files = array();
3102
		
3103
		!isset($this->dirsCache[$path]) && $this->cacheDir($path);
3104
3105
		foreach ($this->dirsCache[$path] as $p) {
3106
			if (($stat = $this->stat($p)) && empty($stat['hidden'])) {
3107
				$files[] = $stat;
3108
			}
3109
		}
3110
3111
		return $files;
3112
	}
3113
	
3114
	
3115
	/**
3116
	 * Return subdirs tree
3117
	 *
3118
	 * @param  string  $path  parent dir path
3119
	 * @param  int     $deep  tree deep
3120
	 * @return array
3121
	 * @author Dmitry (dio) Levashov
3122
	 **/
3123
	protected function gettree($path, $deep, $exclude='') {
3124
		$dirs = array();
3125
		
3126
		!isset($this->dirsCache[$path]) && $this->cacheDir($path);
3127
3128
		foreach ($this->dirsCache[$path] as $p) {
3129
			$stat = $this->stat($p);
3130
			
3131
			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...
3132
				$dirs[] = $stat;
3133
				if ($deep > 0 && !empty($stat['dirs'])) {
3134
					$dirs = array_merge($dirs, $this->gettree($p, $deep-1));
3135
				}
3136
			}
3137
		}
3138
3139
		return $dirs;
3140
	}	
3141
		
3142
	/**
3143
	 * Recursive files search
3144
	 *
3145
	 * @param  string  $path   dir path
3146
	 * @param  string  $q      search string
3147
	 * @param  array   $mimes
3148
	 * @return array
3149
	 * @author Dmitry (dio) Levashov
3150
	 **/
3151
	protected function doSearch($path, $q, $mimes) {
3152
		$result = array();
3153
3154
		foreach($this->scandirCE($path) as $p) {
3155
			@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...
3156
			$stat = $this->stat($p);
3157
3158
			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...
3159
				continue;
3160
			}
3161
3162
			if (!empty($stat['hidden']) || !$this->mimeAccepted($stat['mime'], $mimes)) {
3163
				continue;
3164
			}
3165
			
3166
			$name = $stat['name'];
3167
3168
			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...
3169
				$stat['path'] = $this->path($stat['hash']);
3170 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...
3171
					$path = str_replace($this->separator, '/', substr($p, strlen($this->root) + 1));
3172
					if ($this->encoding) {
3173
						$path = str_replace('%2F', '/', rawurlencode($this->convEncIn($path, true)));
3174
					}
3175
					$stat['url'] = $this->URL . $path;
3176
				}
3177
				
3178
				$result[] = $stat;
3179
			}
3180 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...
3181
				$result = array_merge($result, $this->doSearch($p, $q, $mimes));
3182
			}
3183
		}
3184
		
3185
		return $result;
3186
	}
3187
		
3188
	/**********************  manuipulations  ******************/
3189
		
3190
	/**
3191
	 * Copy file/recursive copy dir only in current volume.
3192
	 * Return new file path or false.
3193
	 *
3194
	 * @param  string  $src   source path
3195
	 * @param  string  $dst   destination dir path
3196
	 * @param  string  $name  new file name (optionaly)
3197
	 * @return string|false
3198
	 * @author Dmitry (dio) Levashov
3199
	 **/
3200
	protected function copy($src, $dst, $name) {
3201
		$srcStat = $this->stat($src);
3202
		$this->clearcache();
3203
		
3204
		if (!empty($srcStat['thash'])) {
3205
			$target = $this->decode($srcStat['thash']);
3206
			if (!$this->inpathCE($target, $this->root)) {
3207
				return $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']), elFinder::ERROR_MKOUTLINK);
3208
			}
3209
			$stat   = $this->stat($target);
3210
			$this->clearcache();
3211
			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...
3212
				? $this->joinPathCE($dst, $name)
3213
				: $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']));
3214
		} 
3215
		
3216
		if ($srcStat['mime'] == 'directory') {
3217
			$test = $this->stat($this->joinPathCE($dst, $name));
3218
			
3219 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...
3220
				return $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']));
3221
			}
3222
			
3223
			$dst = $this->joinPathCE($dst, $name);
3224
			
3225
			foreach ($this->getScandir($src) as $stat) {
3226
				if (empty($stat['hidden'])) {
3227
					$name = $stat['name'];
3228
					if (!$this->copy($this->joinPathCE($src, $name), $dst, $name)) {
3229
						$this->remove($dst, true); // fall back
3230
						return $this->setError($this->error, elFinder::ERROR_COPY, $this->_path($src));
3231
					}
3232
				}
3233
			}
3234
			$this->clearcache();
3235
			return $dst;
3236
		} 
3237
3238
		return $this->convEncOut($this->_copy($this->convEncIn($src), $this->convEncIn($dst), $this->convEncIn($name)))
3239
			? $this->joinPathCE($dst, $name) 
3240
			: $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']));
3241
	}
3242
3243
	/**
3244
	 * Move file
3245
	 * Return new file path or false.
3246
	 *
3247
	 * @param  string  $src   source path
3248
	 * @param  string  $dst   destination dir path
3249
	 * @param  string  $name  new file name 
3250
	 * @return string|false
3251
	 * @author Dmitry (dio) Levashov
3252
	 **/
3253
	protected function move($src, $dst, $name) {
3254
		$stat = $this->stat($src);
3255
		$stat['realpath'] = $src;
3256
		$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...
3257
		$this->clearcache();
3258
		
3259 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...
3260
			$this->removed[] = $stat;
3261
3262
			return $this->joinPathCE($dst, $name);
3263
		}
3264
3265
		return $this->setError(elFinder::ERROR_MOVE, $this->path($stat['hash']));
3266
	}
3267
3268
	/**
3269
	 * Copy file from another volume.
3270
	 * Return new file path or false.
3271
	 *
3272
	 * @param  Object  $volume       source volume
3273
	 * @param  string  $src          source file hash
3274
	 * @param  string  $destination  destination dir path
3275
	 * @param  string  $name         file name
3276
	 * @return string|false
3277
	 * @author Dmitry (dio) Levashov
3278
	 **/
3279
	protected function copyFrom($volume, $src, $destination, $name) {
3280
		
3281
		if (($source = $volume->file($src)) == false) {
3282
			return $this->setError(elFinder::ERROR_COPY, '#'.$src, $volume->error());
3283
		}
3284
		
3285
		$errpath = $volume->path($source['hash']);
3286
		
3287
		if (!$this->nameAccepted($source['name'])) {
3288
			return $this->setError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_INVALID_NAME);
3289
		}
3290
				
3291
		if (!$source['read']) {
3292
			return $this->setError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_PERM_DENIED);
3293
		}
3294
		
3295
		if ($source['mime'] == 'directory') {
3296
			$stat = $this->stat($this->joinPathCE($destination, $name));
3297
			$this->clearcache();
3298 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...
3299
				return $this->setError(elFinder::ERROR_COPY, $errpath);
3300
			}
3301
			
3302
			$path = $this->joinPathCE($destination, $name);
3303
			
3304
			foreach ($volume->scandir($src) as $entr) {
3305
				if (!$this->copyFrom($volume, $entr['hash'], $path, $entr['name'])) {
3306
					$this->remove($path, true); // fall back
3307
					return $this->setError($this->error, elFinder::ERROR_COPY, $errpath);
3308
				}
3309
			}
3310
			
3311
		} else {
3312
			// $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...
3313
			// $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...
3314
			if (($dim = $volume->dimensions($src))) {
3315
				$s = explode('x', $dim);
3316
				$source['width']  = $s[0];
3317
				$source['height'] = $s[1];
3318
			}
3319
			
3320
			if (($fp = $volume->open($src)) == false
3321
			|| ($path = $this->saveCE($fp, $destination, $name, $source)) == false) {
3322
				$fp && $volume->close($fp, $src);
3323
				return $this->setError(elFinder::ERROR_COPY, $errpath);
3324
			}
3325
			$volume->close($fp, $src);
3326
			
3327
			// MIME check
3328
			$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 3321 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...
3329
			if (!$this->allowPutMime($stat['mime'])) {
3330
				$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 3321 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...
3331
				return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME, $errpath);
3332
			}
3333
		}
3334
		
3335
		return $path;
3336
	}
3337
		
3338
	/**
3339
	 * Remove file/ recursive remove dir
3340
	 *
3341
	 * @param  string  $path   file path
3342
	 * @param  bool    $force  try to remove even if file locked
3343
	 * @return bool
3344
	 * @author Dmitry (dio) Levashov
3345
	 **/
3346
	protected function remove($path, $force = false) {
3347
		$stat = $this->stat($path);
3348
		
3349
		if (empty($stat)) {
3350
			return $this->setError(elFinder::ERROR_RM, $this->path($stat['hash']), elFinder::ERROR_FILE_NOT_FOUND);
3351
		}
3352
		
3353
		$stat['realpath'] = $path;
3354
		$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...
3355
		$this->clearcache();
3356
		
3357 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...
3358
			return $this->setError(elFinder::ERROR_LOCKED, $this->path($stat['hash']));
3359
		}
3360
		
3361
		if ($stat['mime'] == 'directory' && empty($stat['thash'])) {
3362
			$ret = $this->delTree($this->convEncIn($path));
3363
			$this->convEncOut();
3364
			if (!$ret) {
3365
				return $this->setError(elFinder::ERROR_RM, $this->path($stat['hash']));
3366
			}
3367 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...
3368
			if ($this->convEncOut(!$this->_unlink($this->convEncIn($path)))) {
3369
				return $this->setError(elFinder::ERROR_RM, $this->path($stat['hash']));
3370
			}
3371
		}
3372
3373
		$this->removed[] = $stat;
3374
		return true;
3375
	}
3376
	
3377
3378
	/************************* thumbnails **************************/
3379
		
3380
	/**
3381
	 * Return thumbnail file name for required file
3382
	 *
3383
	 * @param  array  $stat  file stat
3384
	 * @return string
3385
	 * @author Dmitry (dio) Levashov
3386
	 **/
3387
	protected function tmbname($stat) {
3388
		return $stat['hash'].$stat['ts'].'.png';
3389
	}
3390
	
3391
	/**
3392
	 * Return thumnbnail name if exists
3393
	 *
3394
	 * @param  string  $path file path
3395
	 * @param  array   $stat file stat
3396
	 * @return string|false
3397
	 * @author Dmitry (dio) Levashov
3398
	 **/
3399
	protected function gettmb($path, $stat) {
3400
		if ($this->tmbURL && $this->tmbPath) {
3401
			// file itself thumnbnail
3402
			if (strpos($path, $this->tmbPath) === 0) {
3403
				return basename($path);
3404
			}
3405
3406
			$name = $this->tmbname($stat);
3407
			if (file_exists($this->tmbPath.DIRECTORY_SEPARATOR.$name)) {
3408
				return $name;
3409
			}
3410
		}
3411
		return false;
3412
	}
3413
	
3414
	/**
3415
	 * Return true if thumnbnail for required file can be created
3416
	 *
3417
	 * @param  string  $path  thumnbnail path 
3418
	 * @param  array   $stat  file stat
3419
	 * @param  bool    $checkTmbPath
3420
	 * @return string|bool
3421
	 * @author Dmitry (dio) Levashov
3422
	 **/
3423
	protected function canCreateTmb($path, $stat, $checkTmbPath = true) {
3424
		return (!$checkTmbPath || $this->tmbPathWritable) 
3425
			&& (!$this->tmbPath || strpos($path, $this->tmbPath) === false) // do not create thumnbnail for thumnbnail
3426
			&& $this->imgLib 
3427
			&& strpos($stat['mime'], 'image') === 0 
3428
			&& ($this->imgLib == 'gd' ? $stat['mime'] == 'image/jpeg' || $stat['mime'] == 'image/png' || $stat['mime'] == 'image/gif' : true);
3429
	}
3430
	
3431
	/**
3432
	 * Return true if required file can be resized.
3433
	 * By default - the same as canCreateTmb
3434
	 *
3435
	 * @param  string  $path  thumnbnail path 
3436
	 * @param  array   $stat  file stat
3437
	 * @return string|bool
3438
	 * @author Dmitry (dio) Levashov
3439
	 **/
3440
	protected function canResize($path, $stat) {
3441
		return $this->canCreateTmb($path, $stat, false);
3442
	}
3443
	
3444
	/**
3445
	 * Create thumnbnail and return it's URL on success
3446
	 *
3447
	 * @param  string  $path  file path
3448
	 * @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...
3449
	 * @return string|false
3450
	 * @author Dmitry (dio) Levashov
3451
	 **/
3452
	protected function createTmb($path, $stat) {
3453
		if (!$stat || !$this->canCreateTmb($path, $stat)) {
3454
			return false;
3455
		}
3456
3457
		$name = $this->tmbname($stat);
3458
		$tmb  = $this->tmbPath.DIRECTORY_SEPARATOR.$name;
3459
3460
		// copy image into tmbPath so some drivers does not store files on local fs
3461
		if (($src = $this->fopenCE($path, 'rb')) == false) {
3462
			return false;
3463
		}
3464
3465
		if (($trg = fopen($tmb, 'wb')) == false) {
3466
			$this->fcloseCE($src, $path);
3467
			return false;
3468
		}
3469
3470
		while (!feof($src)) {
3471
			fwrite($trg, fread($src, 8192));
3472
		}
3473
3474
		$this->fcloseCE($src, $path);
3475
		fclose($trg);
3476
3477
		$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...
3478
		
3479
		$tmbSize = $this->tmbSize;
3480
		
3481
		if (($s = getimagesize($tmb)) == false) {
3482
			return false;
3483
		}
3484
3485
		/* If image smaller or equal thumbnail size - just fitting to thumbnail square */
3486
		if ($s[0] <= $tmbSize && $s[1]	<= $tmbSize) {
3487
			$result = $this->imgSquareFit($tmb, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png' );
3488
		} else {
3489
		
3490
			if ($this->options['tmbCrop']) {
3491
		
3492
				$result = $tmb;
3493
				/* Resize and crop if image bigger than thumbnail */
3494 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...
3495
					$result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, false, 'png');
3496
				}
3497
		
3498 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...
3499
					$x = $s[0] > $tmbSize ? intval(($s[0] - $tmbSize)/2) : 0;
3500
					$y = $s[1] > $tmbSize ? intval(($s[1] - $tmbSize)/2) : 0;
3501
					$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...
3502
				} else {
3503
					$result = false;
3504
				}
3505
		
3506
			} else {
3507
				$result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, true, 'png');
3508
			}
3509
		
3510
			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...
3511
				$result = $this->imgSquareFit($result, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png' );
3512
			}
3513
		}
3514
		
3515
		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...
3516
			unlink($tmb);
3517
			return false;
3518
		}
3519
		
3520
		return $name;
3521
	}
3522
3523
	/**
3524
	 * Resize image
3525
	 *
3526
	 * @param  string   $path               image file
3527
	 * @param  int      $width              new width
3528
	 * @param  int      $height             new height
3529
	 * @param  bool	    $keepProportions    crop image
3530
	 * @param  bool	    $resizeByBiggerSide resize image based on bigger side if true
3531
	 * @param  string   $destformat         image destination format
3532
	 * @return string|false
3533
	 * @author Dmitry (dio) Levashov
3534
	 * @author Alexey Sukhotin
3535
	 **/
3536
	protected function imgResize($path, $width, $height, $keepProportions = false, $resizeByBiggerSide = true, $destformat = null) {
3537
		if (($s = @getimagesize($path)) == false) {
3538
			return false;
3539
		}
3540
3541
		$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...
3542
		
3543
		list($size_w, $size_h) = array($width, $height);
3544
	
3545
		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...
3546
		
3547
			list($orig_w, $orig_h) = array($s[0], $s[1]);
3548
		
3549
			/* Resizing by biggest side */
3550
			if ($resizeByBiggerSide) {
3551 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...
3552
					$size_h = round($orig_h * $width / $orig_w);
3553
					$size_w = $width;
3554
				} else {
3555
					$size_w = round($orig_w * $height / $orig_h);
3556
					$size_h = $height;
3557
				}
3558 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...
3559
				if ($orig_w > $orig_h) {
3560
					$size_w = round($orig_w * $height / $orig_h);
3561
					$size_h = $height;
3562
				} else {
3563
					$size_h = round($orig_h * $width / $orig_w);
3564
					$size_w = $width;
3565
				}
3566
			}
3567
		}
3568
3569
		switch ($this->imgLib) {
3570
			case 'imagick':
3571
				
3572
				try {
3573
					$img = new imagick($path);
3574
				} catch (Exception $e) {
3575
					return false;
3576
				}
3577
3578
				// Imagick::FILTER_BOX faster than FILTER_LANCZOS so use for createTmb
3579
				// 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...
3580
				// 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...
3581
				$filter = ($destformat === 'png' /* createTmb */)? Imagick::FILTER_BOX : Imagick::FILTER_LANCZOS;
3582
				
3583
				$ani = ($img->getNumberImages() > 1);
3584
				if ($ani && is_null($destformat)) {
1 ignored issue
show
Coding Style introduced by
As per coding-style, please use === null instead of is_null.
Loading history...
3585
					$img = $img->coalesceImages();
3586
					do {
3587
						$img->resizeImage($size_w, $size_h, $filter, 1);
3588
					} while ($img->nextImage());
3589
					$img = $img->optimizeImageLayers();
3590
					$result = $img->writeImages($path, true);
3591
				} else {
3592
					if ($ani) {
3593
						$img->setFirstIterator();
3594
					}
3595
					$img->resizeImage($size_w, $size_h, $filter, 1);
3596
					$result = $img->writeImage($path);
3597
				}
3598
				
3599
				$img->destroy();
3600
3601
				return $result ? $path : false;
1 ignored issue
show
Coding Style introduced by
Terminating statement must be indented to the same level as the CASE body
Loading history...
3602
3603
				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...
3604
3605
			case 'gd':
3606
				$img = self::gdImageCreate($path,$s['mime']);
3607
3608
				if ($img &&  false != ($tmp = imagecreatetruecolor($size_w, $size_h))) {
3609
				
3610
					self::gdImageBackground($tmp,$this->options['tmbBgColor']);
3611
					
3612
					if (!imagecopyresampled($tmp, $img, 0, 0, 0, 0, $size_w, $size_h, $s[0], $s[1])) {
3613
						return false;
3614
					}
3615
		
3616
					$result = self::gdImage($tmp, $path, $destformat, $s['mime']);
3617
3618
					imagedestroy($img);
3619
					imagedestroy($tmp);
3620
3621
					return $result ? $path : false;
3622
3623
				}
3624
				break;
1 ignored issue
show
Coding Style introduced by
Terminating statement must be indented to the same level as the CASE body
Loading history...
3625
		}
3626
		
3627
		return false;
3628
  	}
3629
  
3630
	/**
3631
	 * Crop image
3632
	 *
3633
	 * @param  string   $path               image file
3634
	 * @param  int      $width              crop width
3635
	 * @param  int      $height             crop height
3636
	 * @param  bool	    $x                  crop left offset
3637
	 * @param  bool	    $y                  crop top offset
3638
	 * @param  string   $destformat         image destination format
3639
	 * @return string|false
3640
	 * @author Dmitry (dio) Levashov
3641
	 * @author Alexey Sukhotin
3642
	 **/
3643
  	protected function imgCrop($path, $width, $height, $x, $y, $destformat = null) {
3644
		if (($s = @getimagesize($path)) == false) {
3645
			return false;
3646
		}
3647
3648
		$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...
3649
		
3650
		switch ($this->imgLib) {
3651
			case 'imagick':
3652
				
3653
				try {
3654
					$img = new imagick($path);
3655
				} catch (Exception $e) {
3656
					return false;
3657
				}
3658
				
3659
				$ani = ($img->getNumberImages() > 1);
3660
				if ($ani && is_null($destformat)) {
1 ignored issue
show
Coding Style introduced by
As per coding-style, please use === null instead of is_null.
Loading history...
3661
					$img = $img->coalesceImages();
3662
					do {
3663
						$img->setImagePage($s[0], $s[1], 0, 0);
3664
						$img->cropImage($width, $height, $x, $y);
3665
						$img->setImagePage($width, $height, 0, 0);
3666
					} while ($img->nextImage());
3667
					$img = $img->optimizeImageLayers();
3668
					$result = $img->writeImages($path, true);
3669
				} else {
3670
					if ($ani) {
3671
						$img->setFirstIterator();
3672
					}
3673
					$img->setImagePage($s[0], $s[1], 0, 0);
3674
					$img->cropImage($width, $height, $x, $y);
3675
					$img->setImagePage($width, $height, 0, 0);
3676
					$result = $img->writeImage($path);
3677
				}
3678
				
3679
				$img->destroy();
3680
3681
				return $result ? $path : false;
1 ignored issue
show
Coding Style introduced by
Terminating statement must be indented to the same level as the CASE body
Loading history...
3682
3683
				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...
3684
3685
			case 'gd':
3686
				$img = self::gdImageCreate($path,$s['mime']);
3687
3688
				if ($img &&  false != ($tmp = imagecreatetruecolor($width, $height))) {
3689
					
3690
					self::gdImageBackground($tmp,$this->options['tmbBgColor']);
3691
3692
					$size_w = $width;
3693
					$size_h = $height;
3694
3695
					if ($s[0] < $width || $s[1] < $height) {
3696
						$size_w = $s[0];
3697
						$size_h = $s[1];
3698
					}
3699
3700
					if (!imagecopy($tmp, $img, 0, 0, $x, $y, $size_w, $size_h)) {
3701
						return false;
3702
					}
3703
					
3704
					$result = self::gdImage($tmp, $path, $destformat, $s['mime']);
3705
3706
					imagedestroy($img);
3707
					imagedestroy($tmp);
3708
3709
					return $result ? $path : false;
3710
3711
				}
3712
				break;
1 ignored issue
show
Coding Style introduced by
Terminating statement must be indented to the same level as the CASE body
Loading history...
3713
		}
3714
3715
		return false;
3716
	}
3717
3718
	/**
3719
	 * Put image to square
3720
	 *
3721
	 * @param  string   $path               image file
3722
	 * @param  int      $width              square width
3723
	 * @param  int      $height             square height
3724
	 * @param  int	    $align              reserved
3725
	 * @param  int 	    $valign             reserved
3726
	 * @param  string   $bgcolor            square background color in #rrggbb format
3727
	 * @param  string   $destformat         image destination format
3728
	 * @return string|false
3729
	 * @author Dmitry (dio) Levashov
3730
	 * @author Alexey Sukhotin
3731
	 **/
3732
	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...
3733
		if (($s = @getimagesize($path)) == false) {
3734
			return false;
3735
		}
3736
3737
		$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...
3738
3739
		/* Coordinates for image over square aligning */
3740
		$y = ceil(abs($height - $s[1]) / 2); 
3741
		$x = ceil(abs($width - $s[0]) / 2);
3742
3743
		switch ($this->imgLib) {
3744
			case 'imagick':
3745
				try {
3746
					$img = new imagick($path);
3747
				} catch (Exception $e) {
3748
					return false;
3749
				}
3750
				
3751
				$ani = ($img->getNumberImages() > 1);
3752
				if ($ani && is_null($destformat)) {
1 ignored issue
show
Coding Style introduced by
As per coding-style, please use === null instead of is_null.
Loading history...
3753
					$img1 = new Imagick();
3754
					$img1->setFormat('gif');
3755
					$img = $img->coalesceImages();
3756
					do {
3757
						$gif = new Imagick();
3758
						$gif->newImage($width, $height, new ImagickPixel($bgcolor));
3759
						$gif->setImageColorspace($img->getImageColorspace());
3760
						$gif->setImageFormat('gif');
3761
						$gif->compositeImage( $img, imagick::COMPOSITE_OVER, $x, $y );
3762
						$gif->setImageDelay($img->getImageDelay());
3763
						$gif->setImageIterations($img->getImageIterations());
3764
						$img1->addImage($gif);
3765
						$gif->destroy();
3766
					} while ($img->nextImage());
3767
					$img1 = $img1->optimizeImageLayers();
3768
					$result = $img1->writeImages($path, true);
3769
				} else {
3770
					if ($ani) {
3771
						$img->setFirstIterator();
3772
					}
3773
					$img1 = new Imagick();
3774
					$img1->newImage($width, $height, new ImagickPixel($bgcolor));
3775
					$img1->setImageColorspace($img->getImageColorspace());
3776
					$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...
3777
					$img1->compositeImage( $img, imagick::COMPOSITE_OVER, $x, $y );
3778
					$result = $img1->writeImage($path);
3779
				}
3780
				
3781
				$img1->destroy();
3782
				$img->destroy();
3783
				return $result ? $path : false;
1 ignored issue
show
Coding Style introduced by
Terminating statement must be indented to the same level as the CASE body
Loading history...
3784
3785
				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...
3786
3787
			case 'gd':
3788
				$img = self::gdImageCreate($path,$s['mime']);
3789
3790
				if ($img &&  false != ($tmp = imagecreatetruecolor($width, $height))) {
3791
3792
					self::gdImageBackground($tmp,$bgcolor);
3793
3794
					if (!imagecopy($tmp, $img, $x, $y, 0, 0, $s[0], $s[1])) {
3795
						return false;
3796
					}
3797
3798
					$result = self::gdImage($tmp, $path, $destformat, $s['mime']);
3799
3800
					imagedestroy($img);
3801
					imagedestroy($tmp);
3802
3803
					return $result ? $path : false;
3804
				}
3805
				break;
1 ignored issue
show
Coding Style introduced by
Terminating statement must be indented to the same level as the CASE body
Loading history...
3806
		}
3807
3808
		return false;
3809
	}
3810
3811
	/**
3812
	 * Rotate image
3813
	 *
3814
	 * @param  string   $path               image file
3815
	 * @param  int      $degree             rotete degrees
3816
	 * @param  string   $bgcolor            square background color in #rrggbb format
3817
	 * @param  string   $destformat         image destination format
3818
	 * @return string|false
3819
	 * @author nao-pon
3820
	 * @author Troex Nevelin
3821
	 **/
3822
	protected function imgRotate($path, $degree, $bgcolor = '#ffffff', $destformat = null) {
3823
		if (($s = @getimagesize($path)) == false || $degree % 360 === 0) {
3824
			return false;
3825
		}
3826
3827
		$result = false;
3828
3829
		// try lossless rotate
3830
		if ($degree % 90 === 0 && in_array($s[2], array(IMAGETYPE_JPEG, IMAGETYPE_JPEG2000))) {
3831
			$count = ($degree / 90) % 4;
3832
			$exiftran = array(
3833
				1 => '-9',
3834
				2 => '-1',
3835
				3 => '-2'
3836
			);
3837
			$jpegtran = array(
3838
				1 => '90',
3839
				2 => '180',
3840
				3 => '270'
3841
			);
3842
			$quotedPath = escapeshellarg($path);
3843
			$cmds = array(
3844
				'exiftran -i '.$exiftran[$count].' '.$path,
3845
				'jpegtran -rotate '.$jpegtran[$count].' -copy all -outfile '.$quotedPath.' '.$quotedPath
3846
			);
3847
			foreach($cmds as $cmd) {
3848
				if ($this->procExec($cmd) === 0) {
3849
					$result = true;
3850
					break;
3851
				}
3852
			}
3853
			if ($result) {
3854
				return $path;
3855
			}
3856
		}
3857
3858
		switch ($this->imgLib) {
3859
			case 'imagick':
3860
				try {
3861
					$img = new imagick($path);
3862
				} catch (Exception $e) {
3863
					return false;
3864
				}
3865
3866
				if ($img->getNumberImages() > 1) {
3867
					$img = $img->coalesceImages();
3868
					do {
3869
						$img->rotateImage(new ImagickPixel($bgcolor), $degree);
3870
					} while ($img->nextImage());
3871
					$img = $img->optimizeImageLayers();
3872
					$result = $img->writeImages($path, true);
3873
				} else {
3874
					$img->rotateImage(new ImagickPixel($bgcolor), $degree);
3875
					$result = $img->writeImage($path);
3876
				}
3877
				$img->destroy();
3878
				return $result ? $path : false;
1 ignored issue
show
Coding Style introduced by
Terminating statement must be indented to the same level as the CASE body
Loading history...
3879
3880
				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...
3881
3882
			case 'gd':
3883
				$img = self::gdImageCreate($path,$s['mime']);
3884
3885
				$degree = 360 - $degree;
3886
				list($r, $g, $b) = sscanf($bgcolor, "#%02x%02x%02x");
3887
				$bgcolor = imagecolorallocate($img, $r, $g, $b);
3888
				$tmp = imageRotate($img, $degree, (int)$bgcolor);
3889
3890
				$result = self::gdImage($tmp, $path, $destformat, $s['mime']);
3891
3892
				imageDestroy($img);
3893
				imageDestroy($tmp);
3894
3895
				return $result ? $path : false;
1 ignored issue
show
Coding Style introduced by
Terminating statement must be indented to the same level as the CASE body
Loading history...
3896
3897
				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...
3898
		}
3899
3900
		return false;
3901
	}
3902
3903
	/**
3904
	 * Execute shell command
3905
	 *
3906
	 * @param  string  $command       command line
3907
	 * @param  array   $output        stdout strings
3908
	 * @param  array   $return_var    process exit code
3909
	 * @param  array   $error_output  stderr strings
3910
	 * @return int     exit code
3911
	 * @author Alexey Sukhotin
3912
	 **/
3913
	protected function procExec($command , array &$output = null, &$return_var = -1, array &$error_output = null) {
3914
3915
		$descriptorspec = array(
3916
			0 => array("pipe", "r"),  // stdin
3917
			1 => array("pipe", "w"),  // stdout
3918
			2 => array("pipe", "w")   // stderr
3919
		);
3920
3921
		$process = proc_open($command, $descriptorspec, $pipes, null, null);
3922
3923
		if (is_resource($process)) {
3924
3925
			fclose($pipes[0]);
3926
3927
			$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...
3928
			$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...
3929
3930
			$output = stream_get_contents($pipes[1]);
3931
			$error_output = stream_get_contents($pipes[2]);
3932
3933
			fclose($pipes[1]);
3934
			fclose($pipes[2]);
3935
			$return_var = proc_close($process);
3936
3937
3938
		}
3939
		
3940
		return $return_var;
3941
		
3942
	}
3943
3944
	/**
3945
	 * Remove thumbnail, also remove recursively if stat is directory
3946
	 *
3947
	 * @param  string  $stat  file stat
3948
	 * @return void
3949
	 * @author Dmitry (dio) Levashov
3950
	 * @author Naoki Sawada
3951
	 * @author Troex Nevelin
3952
	 **/
3953
	protected function rmTmb($stat) {
3954
		if ($stat['mime'] === 'directory') {
3955
			foreach ($this->scandirCE($this->decode($stat['hash'])) as $p) {
3956
				@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...
3957
				$name = $this->basenameCE($p);
3958
				$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...
3959
			}
3960
		} else if (!empty($stat['tmb']) && $stat['tmb'] != "1") {
3961
			$tmb = $this->tmbPath.DIRECTORY_SEPARATOR.$stat['tmb'];
3962
			file_exists($tmb) && @unlink($tmb);
3963
			clearstatcache();
3964
		}
3965
	}
3966
	
3967
	/**
3968
	 * Create an gd image according to the specified mime type
3969
	 *
3970
	 * @param string $path image file
3971
	 * @param string $mime
3972
	 * @return gd image resource identifier
3973
	 */
3974
	protected function gdImageCreate($path,$mime){
3975
		switch($mime){
3976
			case 'image/jpeg':
3977
			return imagecreatefromjpeg($path);
1 ignored issue
show
Coding Style introduced by
Terminating statement must be indented to the same level as the CASE body
Loading history...
3978
3979
			case 'image/png':
3980
			return imagecreatefrompng($path);
1 ignored issue
show
Coding Style introduced by
Terminating statement must be indented to the same level as the CASE body
Loading history...
3981
3982
			case 'image/gif':
3983
			return imagecreatefromgif($path);
1 ignored issue
show
Coding Style introduced by
Terminating statement must be indented to the same level as the CASE body
Loading history...
3984
3985
			case 'image/xbm':
3986
			return imagecreatefromxbm($path);
1 ignored issue
show
Coding Style introduced by
Terminating statement must be indented to the same level as the CASE body
Loading history...
3987
		}
3988
		return false;
3989
	}
3990
3991
	/**
3992
	 * Output gd image to file
3993
	 *
3994
	 * @param resource $image gd image resource
3995
	 * @param string $filename The path to save the file to.
3996
	 * @param string $destformat The Image type to use for $filename
3997
	 * @param string $mime The original image mime type
3998
	 */
3999
	protected function gdImage($image, $filename, $destformat, $mime ){
4000
4001
		if ($destformat == 'jpg' || ($destformat == null && $mime == 'image/jpeg')) {
4002
			return imagejpeg($image, $filename, 100);
4003
		}
4004
4005
		if ($destformat == 'gif' || ($destformat == null && $mime == 'image/gif')) {
4006
			return imagegif($image, $filename, 7);
4007
		}
4008
4009
		return imagepng($image, $filename, 7);
4010
	}
4011
4012
	/**
4013
	 * Assign the proper background to a gd image
4014
	 *
4015
	 * @param resource $image gd image resource
4016
	 * @param string $bgcolor background color in #rrggbb format
4017
	 */
4018
	protected function gdImageBackground($image, $bgcolor){
4019
4020
		if( $bgcolor == 'transparent' ){
4021
			imagesavealpha($image,true);
4022
			$bgcolor1 = imagecolorallocatealpha($image, 255, 255, 255, 127);
4023
4024
		}else{
4025
			list($r, $g, $b) = sscanf($bgcolor, "#%02x%02x%02x");
4026
			$bgcolor1 = imagecolorallocate($image, $r, $g, $b);
4027
		}
4028
4029
		imagefill($image, 0, 0, $bgcolor1);
4030
	}
4031
4032
	/*********************** misc *************************/
4033
	
4034
	/**
4035
	 * Return smart formatted date
4036
	 *
4037
	 * @param  int     $ts  file timestamp
4038
	 * @return string
4039
	 * @author Dmitry (dio) Levashov
4040
	 **/
4041
	// 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...
4042
	// 	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...
4043
	// 		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...
4044
	// 	}
4045
	// 	
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...
4046
	// 	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...
4047
	// 		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...
4048
	// 	} 
4049
	// 	
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...
4050
	// 	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...
4051
	// }
4052
4053
	/**
4054
	* Find position of first occurrence of string in a string with multibyte support
4055
	*
4056
	* @param  string  $haystack  The string being checked.
4057
	* @param  string  $needle    The string to find in haystack.
4058
	* @param  int     $offset    The search offset. If it is not specified, 0 is used.
4059
	* @return int|bool
4060
	* @author Alexey Sukhotin
4061
	**/
4062
	protected function stripos($haystack , $needle , $offset = 0) {
4063
		if (function_exists('mb_stripos')) {
4064
			return mb_stripos($haystack , $needle , $offset);
4065
		} else if (function_exists('mb_strtolower') && function_exists('mb_strpos')) {
4066
			return mb_strpos(mb_strtolower($haystack), mb_strtolower($needle), $offset);
4067
		} 
4068
		return stripos($haystack , $needle , $offset);
4069
	}
4070
4071
	/**
4072
	 * Get server side available archivers
4073
	 * 
4074
	 * @param bool $use_cache
4075
	 * @return array
4076
	 */
4077
	protected function getArchivers($use_cache = true) {
4078
4079
		$sessionKey = 'ARCHIVERS_CACHE';
4080
		if ($use_cache && isset($this->sessionCache[$sessionKey]) && is_array($this->sessionCache[$sessionKey])) {
4081
			return $this->sessionCache[$sessionKey];
4082
		}
4083
		
4084
		$arcs = array(
4085
			'create'  => array(),
4086
			'extract' => array()
4087
		);
4088
		
4089
		if (function_exists('proc_open')) {
4090
		
4091
			$this->procExec('tar --version', $o, $ctar);
4092
			
4093
			if ($ctar == 0) {
4094
				$arcs['create']['application/x-tar']  = array('cmd' => 'tar', 'argc' => '-cf', 'ext' => 'tar');
4095
				$arcs['extract']['application/x-tar'] = array('cmd' => 'tar', 'argc' => '-xf', 'ext' => 'tar');
4096
				unset($o);
4097
				$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...
4098 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...
4099
					$arcs['create']['application/x-gzip']  = array('cmd' => 'tar', 'argc' => '-czf', 'ext' => 'tgz');
4100
					$arcs['extract']['application/x-gzip'] = array('cmd' => 'tar', 'argc' => '-xzf', 'ext' => 'tgz');
4101
				}
4102
				unset($o);
4103
				$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...
4104 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...
4105
					$arcs['create']['application/x-bzip2']  = array('cmd' => 'tar', 'argc' => '-cjf', 'ext' => 'tbz');
4106
					$arcs['extract']['application/x-bzip2'] = array('cmd' => 'tar', 'argc' => '-xjf', 'ext' => 'tbz');
4107
				}
4108
				unset($o);
4109
				$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...
4110 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...
4111
					$arcs['create']['application/x-xz']  = array('cmd' => 'tar', 'argc' => '-cJf', 'ext' => 'xz');
4112
					$arcs['extract']['application/x-xz'] = array('cmd' => 'tar', 'argc' => '-xJf', 'ext' => 'xz');
4113
				}
4114
			}
4115
			unset($o);
4116
			$this->procExec('zip -v', $o, $c);
4117
			if ($c == 0) {
4118
				$arcs['create']['application/zip']  = array('cmd' => 'zip', 'argc' => '-r9', 'ext' => 'zip');
4119
			}
4120
			unset($o);
4121
			$this->procExec('unzip --help', $o, $c);
4122
			if ($c == 0) {
4123
				$arcs['extract']['application/zip'] = array('cmd' => 'unzip', 'argc' => '',  'ext' => 'zip');
4124
			}
4125
			unset($o);
4126
			$this->procExec('rar --version', $o, $c);
4127
			if ($c == 0 || $c == 7) {
4128
				$arcs['create']['application/x-rar']  = array('cmd' => 'rar', 'argc' => 'a -inul', 'ext' => 'rar');
4129
				$arcs['extract']['application/x-rar'] = array('cmd' => 'rar', 'argc' => 'x -y',    'ext' => 'rar');
4130
			} else {
4131
				unset($o);
4132
				$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...
4133
				if ($c==0 || $c == 7) {
4134
					$arcs['extract']['application/x-rar'] = array('cmd' => 'unrar', 'argc' => 'x -y', 'ext' => 'rar');
4135
				}
4136
			}
4137
			unset($o);
4138
			$this->procExec('7za --help', $o, $c);
4139
			if ($c == 0) {
4140
				$arcs['create']['application/x-7z-compressed']  = array('cmd' => '7za', 'argc' => 'a', 'ext' => '7z');
4141
				$arcs['extract']['application/x-7z-compressed'] = array('cmd' => '7za', 'argc' => 'x -y', 'ext' => '7z');
4142
				
4143
				if (empty($arcs['create']['application/zip'])) {
4144
					$arcs['create']['application/zip'] = array('cmd' => '7za', 'argc' => 'a -tzip', 'ext' => 'zip');
4145
				}
4146 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...
4147
					$arcs['extract']['application/zip'] = array('cmd' => '7za', 'argc' => 'x -tzip -y', 'ext' => 'zip');
4148
				}
4149
				if (empty($arcs['create']['application/x-tar'])) {
4150
					$arcs['create']['application/x-tar'] = array('cmd' => '7za', 'argc' => 'a -ttar', 'ext' => 'tar');
4151
				}
4152 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...
4153
					$arcs['extract']['application/x-tar'] = array('cmd' => '7za', 'argc' => 'x -ttar -y', 'ext' => 'tar');
4154
				}
4155
			} else if (substr(PHP_OS,0,3) === 'WIN') {
4156
				// check `7z` for Windows server.
4157
				unset($o);
4158
				$this->procExec('7z', $o, $c);
4159
				if ($c == 0) {
4160
					$arcs['create']['application/x-7z-compressed']  = array('cmd' => '7z', 'argc' => 'a', 'ext' => '7z');
4161
					$arcs['extract']['application/x-7z-compressed'] = array('cmd' => '7z', 'argc' => 'x -y', 'ext' => '7z');
4162
					
4163
					if (empty($arcs['create']['application/zip'])) {
4164
						$arcs['create']['application/zip'] = array('cmd' => '7z', 'argc' => 'a -tzip', 'ext' => 'zip');
4165
					}
4166
					if (empty($arcs['extract']['application/zip'])) {
4167
						$arcs['extract']['application/zip'] = array('cmd' => '7z', 'argc' => 'x -tzip -y', 'ext' => 'zip');
4168
					}
4169
					if (empty($arcs['create']['application/x-tar'])) {
4170
						$arcs['create']['application/x-tar'] = array('cmd' => '7z', 'argc' => 'a -ttar', 'ext' => 'tar');
4171
					}
4172
					if (empty($arcs['extract']['application/x-tar'])) {
4173
						$arcs['extract']['application/x-tar'] = array('cmd' => '7z', 'argc' => 'x -ttar -y', 'ext' => 'tar');
4174
					}
4175
				}
4176
			}
4177
		
4178
		}
4179
		
4180
		// Use PHP ZipArchive Class
4181
		if (class_exists('ZipArchive')) {
4182
			if (empty($arcs['create']['application/zip'])) {
4183
				$arcs['create']['application/zip']  = array('cmd' => 'phpfunction', 'argc' => 'self::zipArchiveZip', 'ext' => 'zip');
4184
			}
4185 View Code Duplication
			if (empty($arcs['extract']['application/zip'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
4186
				$arcs['extract']['application/zip'] = array('cmd' => 'phpfunction', 'argc' => 'self::zipArchiveUnzip', 'ext' => 'zip');
4187
			}
4188
		}
4189
		
4190
		$this->sessionCache[$sessionKey] = $arcs;
4191
		return $arcs;
4192
	}
4193
4194
	/**
4195
	 * Resolve relative / (Unix-like)absolute path
4196
	 * 
4197
	 * @param string $path  target path
4198
	 * @param string $base  base path
4199
	 * @return string
4200
	 */
4201
	protected function getFullPath($path, $base) {
4202
		$separator = $this->separator;
4203
		$systemroot = $this->systemRoot;
4204
4205
		$sepquoted = preg_quote($separator, '#');
4206
4207
		// normalize `/../`
4208
		$normreg = '#('.$sepquoted.')[^'.$sepquoted.']+'.$sepquoted.'\.\.'.$sepquoted.'#';
4209
		while(preg_match($normreg, $path)) {
4210
			$path = preg_replace($normreg, '$1', $path);
4211
		}
4212
		
4213
		// 'Here'
4214
		if ($path === '' || $path === '.' . $separator) return $base;
4215
		
4216
		// Absolute path
4217
		if ($path[0] === $separator || strpos($path, $systemroot) === 0) {
4218
			return $path;
4219
		}
4220
		
4221
		$preg_separator = '#' . $sepquoted . '#';
4222
		
4223
		// Relative path from 'Here'
4224
		if (substr($path, 0, 2) === '.' . $separator || $path[0] !== '.' || substr($path, 0, 3) !== '..' . $separator) {
4225
			$arrn = preg_split($preg_separator, $path, -1, PREG_SPLIT_NO_EMPTY);
4226
			if ($arrn[0] !== '.') {
4227
				array_unshift($arrn, '.');
4228
			}
4229
			$arrn[0] = $base;
4230
			return join($separator, $arrn);
4231
		}
4232
		
4233
		// Relative path from dirname()
4234
		if (substr($path, 0, 3) === '../') {
4235
			$arrn = preg_split($preg_separator, $path, -1, PREG_SPLIT_NO_EMPTY);
4236
			$arrp = preg_split($preg_separator, $base, -1, PREG_SPLIT_NO_EMPTY);
4237
		
4238
			while (! empty($arrn) && $arrn[0] === '..') {
4239
				array_shift($arrn);
4240
				array_pop($arrp);
4241
			}
4242
			$path = ! empty($arrp) ? $systemroot . join($separator, array_merge($arrp, $arrn)) :
4243
				(! empty($arrn) ? $systemroot . join($separator, $arrn) : $systemroot);
4244
		}
4245
		
4246
		return $path;
4247
	}
4248
4249
	/**
4250
	 * Remove directory recursive on local file system
4251
	 *
4252
	 * @param string $dir Target dirctory path
4253
	 * @return boolean
4254
	 * @author Naoki Sawada
4255
	 */
4256
	public function rmdirRecursive($dir) {
4257
		if (!is_link($dir) && is_dir($dir)) {
4258
			@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...
4259
			foreach (array_diff(scandir($dir), array('.', '..')) as $file) {
4260
				@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...
4261
				$path = $dir . DIRECTORY_SEPARATOR . $file;
4262
				if (!is_link($dir) && is_dir($path)) {
4263
					$this->rmdirRecursive($path);
4264
				} else {
4265
					@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...
4266
					@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...
4267
				}
4268
			}
4269
			return @rmdir($dir);
4270
		} else if (is_file($dir) || is_link($dir)) {
4271
			@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...
4272
			return @unlink($dir);
4273
		}
4274
		return false;
4275
	}
4276
4277
	/**
4278
	 * Create archive and return its path
4279
	 *
4280
	 * @param  string  $dir    target dir
4281
	 * @param  array   $files  files names list
4282
	 * @param  string  $name   archive name
4283
	 * @param  array   $arc    archiver options
4284
	 * @return string|bool
4285
	 * @author Dmitry (dio) Levashov, 
4286
	 * @author Alexey Sukhotin
4287
	 * @author Naoki Sawada
4288
	 **/
4289
	protected function makeArchive($dir, $files, $name, $arc) {
4290
		if ($arc['cmd'] === 'phpfunction') {
4291 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...
4292
				call_user_func_array($arc['argc'], array($dir, $files, $name));
4293
			}
4294
		} else {
4295
			$cwd = getcwd();
4296
			chdir($dir);
4297
			
4298
			$files = array_map('escapeshellarg', $files);
4299
			
4300
			$cmd = $arc['cmd'].' '.$arc['argc'].' '.escapeshellarg($name).' '.implode(' ', $files);
4301
			$this->procExec($cmd, $o, $c);
4302
			chdir($cwd);
4303
		}
4304
		$path = $dir.DIRECTORY_SEPARATOR.$name;
4305
		return file_exists($path) ? $path : false;
4306
	}
4307
	
4308
	/**
4309
	 * Unpack archive
4310
	 *
4311
	 * @param  string  $path   archive path
4312
	 * @param  array   $arc    archiver command and arguments (same as in $this->archivers)
4313
	 * @param  bool    $remove remove archive ( unlink($path) )
4314
	 * @return void
4315
	 * @author Dmitry (dio) Levashov
4316
	 * @author Alexey Sukhotin
4317
	 * @author Naoki Sawada
4318
	 **/
4319
	protected function unpackArchive($path, $arc, $remove = true) {
4320
		$dir = dirname($path);
4321
		if ($arc['cmd'] === 'phpfunction') {
4322 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...
4323
				call_user_func_array($arc['argc'], array($path, $dir));
4324
			}
4325
		} else {
4326
			$cwd = getcwd();
4327
			chdir($dir);
4328
			$cmd = $arc['cmd'].' '.$arc['argc'].' '.escapeshellarg(basename($path));
4329
			$this->procExec($cmd, $o, $c);
4330
			chdir($cwd);
4331
		}
4332
		$remove && unlink($path);
4333
	}
4334
	
4335
	/**
4336
	 * Create Zip archive using PHP class ZipArchive
4337
	 * 
4338
	 * @param  string        $dir      target dir
4339
	 * @param  array         $files    files names list
4340
	 * @param  string|object $zipPath  Zip archive name
4341
	 * @return void
4342
	 * @author Naoki Sawada
4343
	 */
4344
	protected static function zipArchiveZip($dir, $files, $zipPath) {
4345
		try {
4346
			if ($start = is_string($zipPath)) {
4347
				$zip = new ZipArchive();
4348
				if ($zip->open($dir . DIRECTORY_SEPARATOR . $zipPath, ZipArchive::CREATE) !== true) {
4349
					$zip = false;
4350
				}
4351
			} else {
4352
				$zip = $zipPath;
4353
			}
4354
			if ($zip) {
4355
				foreach($files as $file) {
4356
					$path = $dir . DIRECTORY_SEPARATOR . $file;
4357
					if (is_dir($path)) {
4358
						$zip->addEmptyDir($file);
4359
						$_files = array();
4360
						if ($handle = opendir($path)) {
4361
							while (false !== ($entry = readdir($handle))) {
4362
								if ($entry !== "." && $entry !== "..") {
4363
									$_files[] = $file . DIRECTORY_SEPARATOR . $entry;
4364
								}
4365
							}
4366
							closedir($handle);
4367
						}
4368
						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...
4369
							self::zipArchiveZip($dir, $_files, $zip);
4370
						}
4371
					} else {
4372
						$zip->addFile($path, $file);
4373
					}
4374
				}
4375
				$start && $zip->close();
4376
			}
4377
		} catch (Exception $e) {
4378
			return false;
4379
		}
4380
		return true;
4381
	}
4382
	
4383
	/**
4384
	 * Unpack Zip archive using PHP class ZipArchive
4385
	 * 
4386
	 * @param  string $zipPath  Zip archive name
4387
	 * @param  string $toDir    Extract to path
4388
	 * @return bool
4389
	 * @author Naoki Sawada
4390
	 */
4391
	protected static function zipArchiveUnzip($zipPath, $toDir) {
4392
		try {
4393
			$zip = new ZipArchive();
4394
			if ($zip->open($zipPath) === true) {
4395
				$zip->extractTo($toDir);
4396
				$zip->close();
4397
			}
4398
		} catch (Exception $e) {
4399
			return false;
4400
		}
4401
		return true;
4402
	}
4403
	
4404
	/**==================================* abstract methods *====================================**/
4405
	
4406
	/*********************** paths/urls *************************/
4407
	
4408
	/**
4409
	 * Return parent directory path
4410
	 *
4411
	 * @param  string  $path  file path
4412
	 * @return string
4413
	 * @author Dmitry (dio) Levashov
4414
	 **/
4415
	abstract protected function _dirname($path);
1 ignored issue
show
Coding Style introduced by
Method name "_dirname" should not be prefixed with an underscore to indicate visibility
Loading history...
4416
4417
	/**
4418
	 * Return file name
4419
	 *
4420
	 * @param  string  $path  file path
4421
	 * @return string
4422
	 * @author Dmitry (dio) Levashov
4423
	 **/
4424
	abstract protected function _basename($path);
1 ignored issue
show
Coding Style introduced by
Method name "_basename" should not be prefixed with an underscore to indicate visibility
Loading history...
4425
4426
	/**
4427
	 * Join dir name and file name and return full path.
4428
	 * Some drivers (db) use int as path - so we give to concat path to driver itself
4429
	 *
4430
	 * @param  string  $dir   dir path
4431
	 * @param  string  $name  file name
4432
	 * @return string
4433
	 * @author Dmitry (dio) Levashov
4434
	 **/
4435
	abstract protected function _joinPath($dir, $name);
1 ignored issue
show
Coding Style introduced by
Method name "_joinPath" should not be prefixed with an underscore to indicate visibility
Loading history...
4436
4437
	/**
4438
	 * Return normalized path 
4439
	 *
4440
	 * @param  string  $path  file path
4441
	 * @return string
4442
	 * @author Dmitry (dio) Levashov
4443
	 **/
4444
	abstract protected function _normpath($path);
1 ignored issue
show
Coding Style introduced by
Method name "_normpath" should not be prefixed with an underscore to indicate visibility
Loading history...
4445
4446
	/**
4447
	 * Return file path related to root dir
4448
	 *
4449
	 * @param  string  $path  file path
4450
	 * @return string
4451
	 * @author Dmitry (dio) Levashov
4452
	 **/
4453
	abstract protected function _relpath($path);
1 ignored issue
show
Coding Style introduced by
Method name "_relpath" should not be prefixed with an underscore to indicate visibility
Loading history...
4454
	
4455
	/**
4456
	 * Convert path related to root dir into real path
4457
	 *
4458
	 * @param  string  $path  rel file path
4459
	 * @return string
4460
	 * @author Dmitry (dio) Levashov
4461
	 **/
4462
	abstract protected function _abspath($path);
1 ignored issue
show
Coding Style introduced by
Method name "_abspath" should not be prefixed with an underscore to indicate visibility
Loading history...
4463
	
4464
	/**
4465
	 * Return fake path started from root dir.
4466
	 * Required to show path on client side.
4467
	 *
4468
	 * @param  string  $path  file path
4469
	 * @return string
4470
	 * @author Dmitry (dio) Levashov
4471
	 **/
4472
	abstract protected function _path($path);
1 ignored issue
show
Coding Style introduced by
Method name "_path" should not be prefixed with an underscore to indicate visibility
Loading history...
4473
	
4474
	/**
4475
	 * Return true if $path is children of $parent
4476
	 *
4477
	 * @param  string  $path    path to check
4478
	 * @param  string  $parent  parent path
4479
	 * @return bool
4480
	 * @author Dmitry (dio) Levashov
4481
	 **/
4482
	abstract protected function _inpath($path, $parent);
1 ignored issue
show
Coding Style introduced by
Method name "_inpath" should not be prefixed with an underscore to indicate visibility
Loading history...
4483
	
4484
	/**
4485
	 * Return stat for given path.
4486
	 * Stat contains following fields:
4487
	 * - (int)    size    file size in b. required
4488
	 * - (int)    ts      file modification time in unix time. required
4489
	 * - (string) mime    mimetype. required for folders, others - optionally
4490
	 * - (bool)   read    read permissions. required
4491
	 * - (bool)   write   write permissions. required
4492
	 * - (bool)   locked  is object locked. optionally
4493
	 * - (bool)   hidden  is object hidden. optionally
4494
	 * - (string) alias   for symlinks - link target path relative to root path. optionally
4495
	 * - (string) target  for symlinks - link target path. optionally
4496
	 *
4497
	 * If file does not exists - returns empty array or false.
4498
	 *
4499
	 * @param  string  $path    file path 
4500
	 * @return array|false
4501
	 * @author Dmitry (dio) Levashov
4502
	 **/
4503
	abstract protected function _stat($path);
1 ignored issue
show
Coding Style introduced by
Method name "_stat" should not be prefixed with an underscore to indicate visibility
Loading history...
4504
	
4505
4506
	/***************** file stat ********************/
4507
4508
		
4509
	/**
4510
	 * Return true if path is dir and has at least one childs directory
4511
	 *
4512
	 * @param  string  $path  dir path
4513
	 * @return bool
4514
	 * @author Dmitry (dio) Levashov
4515
	 **/
4516
	abstract protected function _subdirs($path);
1 ignored issue
show
Coding Style introduced by
Method name "_subdirs" should not be prefixed with an underscore to indicate visibility
Loading history...
4517
	
4518
	/**
4519
	 * Return object width and height
4520
	 * Ususaly used for images, but can be realize for video etc...
4521
	 *
4522
	 * @param  string  $path  file path
4523
	 * @param  string  $mime  file mime type
4524
	 * @return string
4525
	 * @author Dmitry (dio) Levashov
4526
	 **/
4527
	abstract protected function _dimensions($path, $mime);
1 ignored issue
show
Coding Style introduced by
Method name "_dimensions" should not be prefixed with an underscore to indicate visibility
Loading history...
4528
	
4529
	/******************** file/dir content *********************/
4530
4531
	/**
4532
	 * Return files list in directory
4533
	 *
4534
	 * @param  string  $path  dir path
4535
	 * @return array
4536
	 * @author Dmitry (dio) Levashov
4537
	 **/
4538
	abstract protected function _scandir($path);
1 ignored issue
show
Coding Style introduced by
Method name "_scandir" should not be prefixed with an underscore to indicate visibility
Loading history...
4539
	
4540
	/**
4541
	 * Open file and return file pointer
4542
	 *
4543
	 * @param  string  $path  file path
4544
	 * @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...
4545
	 * @return resource|false
4546
	 * @author Dmitry (dio) Levashov
4547
	 **/
4548
	abstract protected function _fopen($path, $mode="rb");
1 ignored issue
show
Coding Style introduced by
Method name "_fopen" should not be prefixed with an underscore to indicate visibility
Loading history...
4549
	
4550
	/**
4551
	 * Close opened file
4552
	 * 
4553
	 * @param  resource  $fp    file pointer
4554
	 * @param  string    $path  file path
4555
	 * @return bool
4556
	 * @author Dmitry (dio) Levashov
4557
	 **/
4558
	abstract protected function _fclose($fp, $path='');
1 ignored issue
show
Coding Style introduced by
Method name "_fclose" should not be prefixed with an underscore to indicate visibility
Loading history...
4559
	
4560
	/********************  file/dir manipulations *************************/
4561
	
4562
	/**
4563
	 * Create dir and return created dir path or false on failed
4564
	 *
4565
	 * @param  string  $path  parent dir path
4566
	 * @param string  $name  new directory name
4567
	 * @return string|bool
4568
	 * @author Dmitry (dio) Levashov
4569
	 **/
4570
	abstract protected function _mkdir($path, $name);
1 ignored issue
show
Coding Style introduced by
Method name "_mkdir" should not be prefixed with an underscore to indicate visibility
Loading history...
4571
	
4572
	/**
4573
	 * Create file and return it's path or false on failed
4574
	 *
4575
	 * @param  string  $path  parent dir path
4576
	 * @param string  $name  new file name
4577
	 * @return string|bool
4578
	 * @author Dmitry (dio) Levashov
4579
	 **/
4580
	abstract protected function _mkfile($path, $name);
1 ignored issue
show
Coding Style introduced by
Method name "_mkfile" should not be prefixed with an underscore to indicate visibility
Loading history...
4581
	
4582
	/**
4583
	 * Create symlink
4584
	 *
4585
	 * @param  string  $source     file to link to
4586
	 * @param  string  $targetDir  folder to create link in
4587
	 * @param  string  $name       symlink name
4588
	 * @return bool
4589
	 * @author Dmitry (dio) Levashov
4590
	 **/
4591
	abstract protected function _symlink($source, $targetDir, $name);
1 ignored issue
show
Coding Style introduced by
Method name "_symlink" should not be prefixed with an underscore to indicate visibility
Loading history...
4592
	
4593
	/**
4594
	 * Copy file into another file (only inside one volume)
4595
	 *
4596
	 * @param  string  $source  source file path
4597
	 * @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...
4598
	 * @param  string  $name    file name
4599
	 * @return bool
4600
	 * @author Dmitry (dio) Levashov
4601
	 **/
4602
	abstract protected function _copy($source, $targetDir, $name);
1 ignored issue
show
Coding Style introduced by
Method name "_copy" should not be prefixed with an underscore to indicate visibility
Loading history...
4603
	
4604
	/**
4605
	 * Move file into another parent dir.
4606
	 * Return new file path or false.
4607
	 *
4608
	 * @param  string  $source  source file path
4609
	 * @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...
4610
	 * @param  string  $name    file name
4611
	 * @return string|bool
4612
	 * @author Dmitry (dio) Levashov
4613
	 **/
4614
	abstract protected function _move($source, $targetDir, $name);
1 ignored issue
show
Coding Style introduced by
Method name "_move" should not be prefixed with an underscore to indicate visibility
Loading history...
4615
	
4616
	/**
4617
	 * Remove file
4618
	 *
4619
	 * @param  string  $path  file path
4620
	 * @return bool
4621
	 * @author Dmitry (dio) Levashov
4622
	 **/
4623
	abstract protected function _unlink($path);
1 ignored issue
show
Coding Style introduced by
Method name "_unlink" should not be prefixed with an underscore to indicate visibility
Loading history...
4624
4625
	/**
4626
	 * Remove dir
4627
	 *
4628
	 * @param  string  $path  dir path
4629
	 * @return bool
4630
	 * @author Dmitry (dio) Levashov
4631
	 **/
4632
	abstract protected function _rmdir($path);
1 ignored issue
show
Coding Style introduced by
Method name "_rmdir" should not be prefixed with an underscore to indicate visibility
Loading history...
4633
4634
	/**
4635
	 * Create new file and write into it from file pointer.
4636
	 * Return new file path or false on error.
4637
	 *
4638
	 * @param  resource  $fp   file pointer
4639
	 * @param  string    $dir  target dir path
4640
	 * @param  string    $name file name
4641
	 * @param  array     $stat file stat (required by some virtual fs)
4642
	 * @return bool|string
4643
	 * @author Dmitry (dio) Levashov
4644
	 **/
4645
	abstract protected function _save($fp, $dir, $name, $stat);
1 ignored issue
show
Coding Style introduced by
Method name "_save" should not be prefixed with an underscore to indicate visibility
Loading history...
4646
	
4647
	/**
4648
	 * Get file contents
4649
	 *
4650
	 * @param  string  $path  file path
4651
	 * @return string|false
4652
	 * @author Dmitry (dio) Levashov
4653
	 **/
4654
	abstract protected function _getContents($path);
1 ignored issue
show
Coding Style introduced by
Method name "_getContents" should not be prefixed with an underscore to indicate visibility
Loading history...
4655
	
4656
	/**
4657
	 * Write a string to a file
4658
	 *
4659
	 * @param  string  $path     file path
4660
	 * @param  string  $content  new file content
4661
	 * @return bool
4662
	 * @author Dmitry (dio) Levashov
4663
	 **/
4664
	abstract protected function _filePutContents($path, $content);
1 ignored issue
show
Coding Style introduced by
Method name "_filePutContents" should not be prefixed with an underscore to indicate visibility
Loading history...
4665
4666
	/**
4667
	 * Extract files from archive
4668
	 *
4669
	 * @param  string  $path file path
4670
	 * @param  array   $arc  archiver options
4671
	 * @return bool
4672
	 * @author Dmitry (dio) Levashov, 
4673
	 * @author Alexey Sukhotin
4674
	 **/
4675
	abstract protected function _extract($path, $arc);
1 ignored issue
show
Coding Style introduced by
Method name "_extract" should not be prefixed with an underscore to indicate visibility
Loading history...
4676
4677
	/**
4678
	 * Create archive and return its path
4679
	 *
4680
	 * @param  string  $dir    target dir
4681
	 * @param  array   $files  files names list
4682
	 * @param  string  $name   archive name
4683
	 * @param  array   $arc    archiver options
4684
	 * @return string|bool
4685
	 * @author Dmitry (dio) Levashov, 
4686
	 * @author Alexey Sukhotin
4687
	 **/
4688
	abstract protected function _archive($dir, $files, $name, $arc);
1 ignored issue
show
Coding Style introduced by
Method name "_archive" should not be prefixed with an underscore to indicate visibility
Loading history...
4689
4690
	/**
4691
	 * Detect available archivers
4692
	 *
4693
	 * @return void
4694
	 * @author Dmitry (dio) Levashov, 
4695
	 * @author Alexey Sukhotin
4696
	 **/
4697
	abstract protected function _checkArchivers();
1 ignored issue
show
Coding Style introduced by
Method name "_checkArchivers" should not be prefixed with an underscore to indicate visibility
Loading history...
4698
4699
	/**
4700
	 * Change file mode (chmod)
4701
	 *
4702
	 * @param  string  $path  file path
4703
	 * @param  string  $mode  octal string such as '0755'
4704
	 * @return bool
4705
	 * @author David Bartle,
4706
	 **/
4707
	abstract protected function _chmod($path, $mode);
1 ignored issue
show
Coding Style introduced by
Method name "_chmod" should not be prefixed with an underscore to indicate visibility
Loading history...
4708
4709
	
4710
} // END class
2 ignored issues
show
Coding Style introduced by
According to PSR2, the closing brace of classes should be placed on the next line directly after the body.

Below you find some examples:

// Incorrect placement according to PSR2
class MyClass
{
    public function foo()
    {

    }
    // This blank line is not allowed.

}

// Correct
class MyClass
{
    public function foo()
    {

    } // No blank lines after this line.
}
Loading history...
Coding Style introduced by
The closing brace of a class hould be on a line by itself.
Loading history...
4711