Completed
Push — 2.x ( 6cb567...091efb )
by Naoki
05:17
created

elFinderVolumeDriver::nameAccepted()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 15
Code Lines 10

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 15
rs 8.8571
cc 5
eloc 10
nc 5
nop 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...
15
	
16
	/**
17
	 * Request args
18
	 * $_POST or $_GET values
19
	 * 
20
	 * @var array
21
	 */
22
	protected $ARGS = array();
23
	
24
	/**
25
	 * Driver id
26
	 * Must be started from letter and contains [a-z0-9]
27
	 * Used as part of volume id
28
	 *
29
	 * @var string
30
	 **/
31
	protected $driverId = 'a';
32
	
33
	/**
34
	 * Volume id - used as prefix for files hashes
35
	 *
36
	 * @var string
37
	 **/
38
	protected $id = '';
39
	
40
	/**
41
	 * Flag - volume "mounted" and available
42
	 *
43
	 * @var bool
44
	 **/
45
	protected $mounted = false;
46
	
47
	/**
48
	 * Root directory path
49
	 *
50
	 * @var string
51
	 **/
52
	protected $root = '';
53
	
54
	/**
55
	 * Root basename | alias
56
	 *
57
	 * @var string
58
	 **/
59
	protected $rootName = '';
60
	
61
	/**
62
	 * Default directory to open
63
	 *
64
	 * @var string
65
	 **/
66
	protected $startPath = '';
67
	
68
	/**
69
	 * Base URL
70
	 *
71
	 * @var string
72
	 **/
73
	protected $URL = '';
74
	
75
	/**
76
	 * Thumbnails dir path
77
	 *
78
	 * @var string
79
	 **/
80
	protected $tmbPath = '';
81
	
82
	/**
83
	 * Is thumbnails dir writable
84
	 *
85
	 * @var bool
86
	 **/
87
	protected $tmbPathWritable = false;
88
	
89
	/**
90
	 * Thumbnails base URL
91
	 *
92
	 * @var string
93
	 **/
94
	protected $tmbURL = '';
95
	
96
	/**
97
	 * Thumbnails size in px
98
	 *
99
	 * @var int
100
	 **/
101
	protected $tmbSize = 48;
102
	
103
	/**
104
	 * Image manipulation lib name
105
	 * auto|imagick|mogtify|gd
106
	 *
107
	 * @var string
108
	 **/
109
	protected $imgLib = 'auto';
110
	
111
	/**
112
	 * Library to crypt files name
113
	 *
114
	 * @var string
115
	 **/
116
	protected $cryptLib = '';
117
	
118
	/**
119
	 * Archivers config
120
	 *
121
	 * @var array
122
	 **/
123
	protected $archivers = array(
124
		'create'  => array(),
125
		'extract' => array()
126
	);
127
	
128
	/**
129
	 * Server character encoding
130
	 *
131
	 * @var string or null
132
	 **/
133
	protected $encoding = null;
134
	
135
	/**
136
	 * How many subdirs levels return for tree
137
	 *
138
	 * @var int
139
	 **/
140
	protected $treeDeep = 1;
141
	
142
	/**
143
	 * Errors from last failed action
144
	 *
145
	 * @var array
146
	 **/
147
	protected $error = array();
148
	
149
	/**
150
	 * Today 24:00 timestamp
151
	 *
152
	 * @var int
153
	 **/
154
	protected $today = 0;
155
	
156
	/**
157
	 * Yesterday 24:00 timestamp
158
	 *
159
	 * @var int
160
	 **/
161
	protected $yesterday = 0;
162
	
163
	/**
164
	 * Force make dirctory on extract
165
	 *
166
	 * @var int
167
	 **/
168
	protected $extractToNewdir = 'auto';
169
	
170
	/**
171
	 * Object configuration
172
	 *
173
	 * @var array
174
	 **/
175
	protected $options = array(
176
		'id'              => '',
177
		// root directory path
178
		'path'            => '',
179
		// open this path on initial request instead of root path
180
		'startPath'       => '',
181
		// how many subdirs levels return per request
182
		'treeDeep'        => 1,
183
		// root url, not set to disable sending URL to client (replacement for old "fileURL" option)
184
		'URL'             => '',
185
		// directory separator. required by client to show paths correctly
186
		'separator'       => DIRECTORY_SEPARATOR,
187
		// Server character encoding (default is '': UTF-8)
188
		'encoding'        => '',
189
		// for convert character encoding (default is '': Not change locale)
190
		'locale'          => '',
191
		// URL of volume icon (16x16 pixel image file)
192
		'icon'            => '',
193
		// CSS Class of volume root in tree
194
		'rootCssClass'    => '',
195
		// library to crypt/uncrypt files names (not implemented)
196
		'cryptLib'        => '',
197
		// how to detect files mimetypes. (auto/internal/finfo/mime_content_type)
198
		'mimeDetect'      => 'auto',
199
		// mime.types file path (for mimeDetect==internal)
200
		'mimefile'        => '',
201
		// mime type normalize map : Array '[ext]:[detected mime type]' => '[normalized mime]'
202
		'mimeMap'         => array(
203
		                     'md:application/x-genesis-rom' => 'text/x-markdown',
204
		                     'md:text/plain'                => 'text/x-markdown',
205
		                     'markdown:text/plain'          => 'text/x-markdown',
206
		                     'css:text/x-asm'               => 'text/css'
207
		                    ),
208
		// MIME regex of send HTTP header "Content-Disposition: inline"
209
		// '.' is allow inline of all of MIME types
210
		// '$^' is not allow inline of all of MIME types
211
		'dispInlineRegex' => '^(?:(?:image|text)|application/x-shockwave-flash$)',
212
		// directory for thumbnails
213
		'tmbPath'         => '.tmb',
214
		// mode to create thumbnails dir
215
		'tmbPathMode'     => 0777,
216
		// thumbnails dir URL. Set it if store thumbnails outside root directory
217
		'tmbURL'          => '',
218
		// thumbnails size (px)
219
		'tmbSize'         => 48,
220
		// thumbnails crop (true - crop, false - scale image to fit thumbnail size)
221
		'tmbCrop'         => true,
222
		// thumbnails background color (hex #rrggbb or 'transparent')
223
		'tmbBgColor'      => '#ffffff',
224
		// image manipulations library
225
		'imgLib'          => 'auto',
226
		// on paste file -  if true - old file will be replaced with new one, if false new file get name - original_name-number.ext
227
		'copyOverwrite'   => true,
228
		// if true - join new and old directories content on paste
229
		'copyJoin'        => true,
230
		// on upload -  if true - old file will be replaced with new one, if false new file get name - original_name-number.ext
231
		'uploadOverwrite' => true,
232
		// mimetypes allowed to upload
233
		'uploadAllow'     => array(),
234
		// mimetypes not allowed to upload
235
		'uploadDeny'      => array(),
236
		// order to proccess uploadAllow and uploadDeny options
237
		'uploadOrder'     => array('deny', 'allow'),
238
		// maximum upload file size. NOTE - this is size for every uploaded files
239
		'uploadMaxSize'   => 0,
240
		// files dates format
241
		'dateFormat'      => 'j M Y H:i',
242
		// files time format
243
		'timeFormat'      => 'H:i',
244
		// if true - every folder will be check for children folders, otherwise all folders will be marked as having subfolders
245
		'checkSubfolders' => true,
246
		// allow to copy from this volume to other ones?
247
		'copyFrom'        => true,
248
		// allow to copy from other volumes to this one?
249
		'copyTo'          => true,
250
		// list of commands disabled on this root
251
		'disabled'        => array(),
252
		// enable file owner, group & mode info, `false` to inactivate "chmod" command.
253
		'statOwner'       => false,
254
		// allow exec chmod of read-only files
255
		'allowChmodReadOnly' => false,
256
		// regexp or function name to validate new file name
257
		'acceptedName'    => '/^[^\.].*/', //<-- DONT touch this! Use constructor options to overwrite it!
258
		// function/class method to control files permissions
259
		'accessControl'   => null,
260
		// some data required by access control
261
		'accessControlData' => null,
262
		// default permissions.
263
		'defaults'     => array(
264
			'read'   => true,
265
			'write'  => true,
266
			'locked' => false,
267
			'hidden' => false
268
		),
269
		// files attributes
270
		'attributes'   => array(),
271
		// Allowed archive's mimetypes to create. Leave empty for all available types.
272
		'archiveMimes' => array(),
273
		// Manual config for archivers. See example below. Leave empty for auto detect
274
		'archivers'    => array(),
275
		// plugin settings
276
		'plugin'       => array(),
277
		// required to fix bug on macos
278
		'utf8fix'      => false,
279
		 //                           й                 ё              Й               Ё              Ø         Å
280
		'utf8patterns' => array("\u0438\u0306", "\u0435\u0308", "\u0418\u0306", "\u0415\u0308", "\u00d8A", "\u030a"),
281
		'utf8replace'  => array("\u0439",        "\u0451",       "\u0419",       "\u0401",       "\u00d8", "\u00c5")
282
	);
283
284
	/**
285
	 * Defaults permissions
286
	 *
287
	 * @var array
288
	 **/
289
	protected $defaults = array(
290
		'read'   => true,
291
		'write'  => true,
292
		'locked' => false,
293
		'hidden' => false
294
	);
295
	
296
	/**
297
	 * Access control function/class
298
	 *
299
	 * @var mixed
300
	 **/
301
	protected $attributes = array();
302
	
303
	/**
304
	 * Access control function/class
305
	 *
306
	 * @var mixed
307
	 **/
308
	protected $access = null;
309
	
310
	/**
311
	 * Mime types allowed to upload
312
	 *
313
	 * @var array
314
	 **/
315
	protected $uploadAllow = array();
316
	
317
	/**
318
	 * Mime types denied to upload
319
	 *
320
	 * @var array
321
	 **/
322
	protected $uploadDeny = array();
323
	
324
	/**
325
	 * Order to validate uploadAllow and uploadDeny
326
	 *
327
	 * @var array
328
	 **/
329
	protected $uploadOrder = array();
330
	
331
	/**
332
	 * Maximum allowed upload file size.
333
	 * Set as number or string with unit - "10M", "500K", "1G"
334
	 *
335
	 * @var int|string
336
	 **/
337
	protected $uploadMaxSize = 0;
338
	
339
	/**
340
	 * Mimetype detect method
341
	 *
342
	 * @var string
343
	 **/
344
	protected $mimeDetect = 'auto';
345
	
346
	/**
347
	 * Flag - mimetypes from externail file was loaded
348
	 *
349
	 * @var bool
350
	 **/
351
	private static $mimetypesLoaded = false;
352
	
353
	/**
354
	 * Finfo object for mimeDetect == 'finfo'
355
	 *
356
	 * @var object
357
	 **/
358
	protected $finfo = null;
359
	
360
	/**
361
	 * List of disabled client's commands
362
	 *
363
	 * @var array
364
	 **/
365
	protected $disabled = array();
366
	
367
	/**
368
	 * default extensions/mimetypes for mimeDetect == 'internal' 
369
	 *
370
	 * @var array
371
	 **/
372
	protected static $mimetypes = array(
373
		// applications
374
		'ai'    => 'application/postscript',
375
		'eps'   => 'application/postscript',
376
		'exe'   => 'application/x-executable',
377
		'doc'   => 'application/msword',
378
		'dot'   => 'application/msword',
379
		'xls'   => 'application/vnd.ms-excel',
380
		'xlt'   => 'application/vnd.ms-excel',
381
		'xla'   => 'application/vnd.ms-excel',
382
		'ppt'   => 'application/vnd.ms-powerpoint',
383
		'pps'   => 'application/vnd.ms-powerpoint',
384
		'pdf'   => 'application/pdf',
385
		'xml'   => 'application/xml',
386
		'swf'   => 'application/x-shockwave-flash',
387
		'torrent' => 'application/x-bittorrent',
388
		'jar'   => 'application/x-jar',
389
		// open office (finfo detect as application/zip)
390
		'odt'   => 'application/vnd.oasis.opendocument.text',
391
		'ott'   => 'application/vnd.oasis.opendocument.text-template',
392
		'oth'   => 'application/vnd.oasis.opendocument.text-web',
393
		'odm'   => 'application/vnd.oasis.opendocument.text-master',
394
		'odg'   => 'application/vnd.oasis.opendocument.graphics',
395
		'otg'   => 'application/vnd.oasis.opendocument.graphics-template',
396
		'odp'   => 'application/vnd.oasis.opendocument.presentation',
397
		'otp'   => 'application/vnd.oasis.opendocument.presentation-template',
398
		'ods'   => 'application/vnd.oasis.opendocument.spreadsheet',
399
		'ots'   => 'application/vnd.oasis.opendocument.spreadsheet-template',
400
		'odc'   => 'application/vnd.oasis.opendocument.chart',
401
		'odf'   => 'application/vnd.oasis.opendocument.formula',
402
		'odb'   => 'application/vnd.oasis.opendocument.database',
403
		'odi'   => 'application/vnd.oasis.opendocument.image',
404
		'oxt'   => 'application/vnd.openofficeorg.extension',
405
		// MS office 2007 (finfo detect as application/zip)
406
		'docx'  => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
407
		'docm'  => 'application/vnd.ms-word.document.macroEnabled.12',
408
		'dotx'  => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
409
		'dotm'  => 'application/vnd.ms-word.template.macroEnabled.12',
410
		'xlsx'  => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
411
		'xlsm'  => 'application/vnd.ms-excel.sheet.macroEnabled.12',
412
		'xltx'  => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
413
		'xltm'  => 'application/vnd.ms-excel.template.macroEnabled.12',
414
		'xlsb'  => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
415
		'xlam'  => 'application/vnd.ms-excel.addin.macroEnabled.12',
416
		'pptx'  => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
417
		'pptm'  => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
418
		'ppsx'  => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
419
		'ppsm'  => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12',
420
		'potx'  => 'application/vnd.openxmlformats-officedocument.presentationml.template',
421
		'potm'  => 'application/vnd.ms-powerpoint.template.macroEnabled.12',
422
		'ppam'  => 'application/vnd.ms-powerpoint.addin.macroEnabled.12',
423
		'sldx'  => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
424
		'sldm'  => 'application/vnd.ms-powerpoint.slide.macroEnabled.12',
425
		// archives
426
		'gz'    => 'application/x-gzip',
427
		'tgz'   => 'application/x-gzip',
428
		'bz'    => 'application/x-bzip2',
429
		'bz2'   => 'application/x-bzip2',
430
		'tbz'   => 'application/x-bzip2',
431
		'xz'    => 'application/x-xz',
432
		'zip'   => 'application/zip',
433
		'rar'   => 'application/x-rar',
434
		'tar'   => 'application/x-tar',
435
		'7z'    => 'application/x-7z-compressed',
436
		// texts
437
		'txt'   => 'text/plain',
438
		'php'   => 'text/x-php',
439
		'html'  => 'text/html',
440
		'htm'   => 'text/html',
441
		'js'    => 'text/javascript',
442
		'css'   => 'text/css',
443
		'rtf'   => 'text/rtf',
444
		'rtfd'  => 'text/rtfd',
445
		'py'    => 'text/x-python',
446
		'java'  => 'text/x-java-source',
447
		'rb'    => 'text/x-ruby',
448
		'sh'    => 'text/x-shellscript',
449
		'pl'    => 'text/x-perl',
450
		'xml'   => 'text/xml',
451
		'sql'   => 'text/x-sql',
452
		'c'     => 'text/x-csrc',
453
		'h'     => 'text/x-chdr',
454
		'cpp'   => 'text/x-c++src',
455
		'hh'    => 'text/x-c++hdr',
456
		'log'   => 'text/plain',
457
		'csv'   => 'text/x-comma-separated-values',
458
		'md'    => 'text/x-markdown',
459
		'markdown' => 'text/x-markdown',
460
		// images
461
		'bmp'   => 'image/x-ms-bmp',
462
		'jpg'   => 'image/jpeg',
463
		'jpeg'  => 'image/jpeg',
464
		'gif'   => 'image/gif',
465
		'png'   => 'image/png',
466
		'tif'   => 'image/tiff',
467
		'tiff'  => 'image/tiff',
468
		'tga'   => 'image/x-targa',
469
		'psd'   => 'image/vnd.adobe.photoshop',
470
		'ai'    => 'image/vnd.adobe.photoshop',
471
		'xbm'   => 'image/xbm',
472
		'pxm'   => 'image/pxm',
473
		//audio
474
		'mp3'   => 'audio/mpeg',
475
		'mid'   => 'audio/midi',
476
		'ogg'   => 'audio/ogg',
477
		'oga'   => 'audio/ogg',
478
		'm4a'   => 'audio/x-m4a',
479
		'wav'   => 'audio/wav',
480
		'wma'   => 'audio/x-ms-wma',
481
		// video
482
		'avi'   => 'video/x-msvideo',
483
		'dv'    => 'video/x-dv',
484
		'mp4'   => 'video/mp4',
485
		'mpeg'  => 'video/mpeg',
486
		'mpg'   => 'video/mpeg',
487
		'mov'   => 'video/quicktime',
488
		'wm'    => 'video/x-ms-wmv',
489
		'flv'   => 'video/x-flv',
490
		'mkv'   => 'video/x-matroska',
491
		'webm'  => 'video/webm',
492
		'ogv'   => 'video/ogg',
493
		'ogm'   => 'video/ogg'
494
		);
495
	
496
	/**
497
	 * Directory separator - required by client
498
	 *
499
	 * @var string
500
	 **/
501
	protected $separator = DIRECTORY_SEPARATOR;
502
	
503
	/**
504
	 * System Root path (Unix like: '/', Windows: '\', 'C:\' or 'D:\'...)
505
	 *
506
	 * @var string
507
	 **/
508
	protected $systemRoot = DIRECTORY_SEPARATOR;
509
	
510
	/**
511
	 * Mimetypes allowed to display
512
	 *
513
	 * @var array
514
	 **/
515
	protected $onlyMimes = array();
516
	
517
	/**
518
	 * Store files moved or overwrited files info
519
	 *
520
	 * @var array
521
	 **/
522
	protected $removed = array();
523
	
524
	/**
525
	 * Cache storage
526
	 *
527
	 * @var array
528
	 **/
529
	protected $cache = array();
530
	
531
	/**
532
	 * Cache by folders
533
	 *
534
	 * @var array
535
	 **/
536
	protected $dirsCache = array();
537
	
538
	/**
539
	 * Cache for subdirsCE()
540
	 * 
541
	 * @var array
542
	 */
543
	protected $subdirsCache = array();
544
	
545
	/**
546
	 * Reference of $_SESSION[elFinder::$sessionCacheKey][$this->id]
547
	 * 
548
	 * @var array
549
	 */
550
	protected $sessionCache;
551
	
552
	/*********************************************************************/
553
	/*                            INITIALIZATION                         */
554
	/*********************************************************************/
555
	
556
	/**
557
	 * Prepare driver before mount volume.
558
	 * Return true if volume is ready.
559
	 *
560
	 * @return bool
561
	 * @author Dmitry (dio) Levashov
562
	 **/
563
	protected function init() {
564
		return true;
565
	}	
566
		
567
	/**
568
	 * Configure after successfull mount.
569
	 * By default set thumbnails path and image manipulation library.
570
	 *
571
	 * @return void
572
	 * @author Dmitry (dio) Levashov
573
	 **/
574
	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...
575
		// set ARGS
576
		$this->ARGS = $_SERVER['REQUEST_METHOD'] === 'POST'? $_POST : $_GET;
577
		// set thumbnails path
578
		$path = $this->options['tmbPath'];
579
		if ($path) {
580 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...
581
				if (@mkdir($path)) {
582
					chmod($path, $this->options['tmbPathMode']);
583
				} else {
584
					$path = '';
585
				}
586
			} 
587
			
588
			if (is_dir($path) && is_readable($path)) {
589
				$this->tmbPath = $path;
590
				$this->tmbPathWritable = is_writable($path);
591
			}
592
		}
593
594
		// set image manipulation library
595
		$type = preg_match('/^(imagick|gd|auto)$/i', $this->options['imgLib'])
596
			? strtolower($this->options['imgLib'])
597
			: 'auto';
598
599
		if (($type == 'imagick' || $type == 'auto') && extension_loaded('imagick')) {
600
			$this->imgLib = 'imagick';
601
		} else {
602
			$this->imgLib = function_exists('gd_info') ? 'gd' : '';
603
		}
604
		
605
		// check 'statOwner' for command `chmod`
606
		if (empty($this->options['statOwner'])) {
607
			$this->disabled[] ='chmod';
608
		}
609
		
610
		// check 'mimeMap'
611
		if (!is_array($this->options['mimeMap'])) {
612
			$this->options['mimeMap'] = array();
613
		}
614
	}
615
	
616
	
617
	/*********************************************************************/
618
	/*                              PUBLIC API                           */
619
	/*********************************************************************/
620
	
621
	/**
622
	 * Return driver id. Used as a part of volume id.
623
	 *
624
	 * @return string
625
	 * @author Dmitry (dio) Levashov
626
	 **/
627
	public function driverId() {
628
		return $this->driverId;
629
	}
630
	
631
	/**
632
	 * Return volume id
633
	 *
634
	 * @return string
635
	 * @author Dmitry (dio) Levashov
636
	 **/
637
	public function id() {
638
		return $this->id;
639
	}
640
		
641
	/**
642
	 * Return debug info for client
643
	 *
644
	 * @return array
645
	 * @author Dmitry (dio) Levashov
646
	 **/
647
	public function debug() {
648
		return array(
649
			'id'         => $this->id(),
650
			'name'       => strtolower(substr(get_class($this), strlen('elfinderdriver'))),
651
			'mimeDetect' => $this->mimeDetect,
652
			'imgLib'     => $this->imgLib
653
		);
654
	}
655
656
	/**
657
	 * chmod a file or folder
658
	 *
659
	 * @param  string   $hash    file or folder hash to chmod
660
	 * @param  string   $mode    octal string representing new permissions
661
	 * @return array|false
662
	 * @author David Bartle
663
	 **/
664
	public function chmod($hash, $mode) {
665
		if ($this->commandDisabled('chmod')) {
666
			return $this->setError(elFinder::ERROR_PERM_DENIED);
667
		}
668
669
		if (!($file = $this->file($hash))) {
670
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
671
		}
672
673
		if (!$this->options['allowChmodReadOnly']) {
674 View Code Duplication
			if (!$this->attr($this->decode($hash), 'write', null, ($file['mime'] === 'directory'))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
675
				return $this->setError(elFinder::ERROR_PERM_DENIED, $file['name']);
676
			}
677
		}
678
679
		$path = $this->decode($hash);
680
681 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...
682
			return $this->setError(elFinder::ERROR_PERM_DENIED, $file['name']);
683
		}
684
685
		$this->clearcache();
686
687
		if ($file = $this->stat($path)) {
688
			$files = array($file);
689
			if ($file['mime'] === 'directory' && $write !== $file['write']) {
0 ignored issues
show
Bug introduced by
The variable $write does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
690
				foreach ($this->getScandir($path) as $stat) {
691
					if ($this->mimeAccepted($stat['mime'])) {
692
						$files[] = $stat;
693
					}
694
				}
695
			}
696
			return $files;
697
		} else {
698
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
699
		}
700
	}
701
	
702
	/**
703
	 * "Mount" volume.
704
	 * Return true if volume available for read or write, 
705
	 * false - otherwise
706
	 *
707
	 * @return bool
708
	 * @author Dmitry (dio) Levashov
709
	 * @author Alexey Sukhotin
710
	 **/
711
	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...
712
		if (!isset($opts['path']) || $opts['path'] === '') {
713
			return $this->setError('Path undefined.');;
714
		}
715
		
716
		$this->options = array_merge($this->options, $opts);
717
		$this->id = $this->driverId.(!empty($this->options['id']) ? $this->options['id'] : elFinder::$volumesCnt++).'_';
718
		$this->root = $this->normpathCE($this->options['path']);
719
		$this->separator = isset($this->options['separator']) ? $this->options['separator'] : DIRECTORY_SEPARATOR;
720
		$this->systemRoot = isset($this->options['systemRoot']) ? $this->options['systemRoot'] : $this->separator;
721
		
722
		// set server encoding
723
		if (!empty($this->options['encoding']) && strtoupper($this->options['encoding']) !== 'UTF-8') {
724
			$this->encoding = $this->options['encoding'];
725
		} else {
726
			$this->encoding = null;
727
		}
728
		
729
		$argInit = !empty($this->ARGS['init']);
730
		
731
		// session cache
732
		if ($argInit || ! isset($_SESSION[elFinder::$sessionCacheKey][$this->id])) {
733
			$_SESSION[elFinder::$sessionCacheKey][$this->id] = array();
734
		}
735
		$this->sessionCache = &$_SESSION[elFinder::$sessionCacheKey][$this->id];
736
		
737
		// default file attribute
738
		$this->defaults = array(
739
			'read'    => isset($this->options['defaults']['read'])  ? !!$this->options['defaults']['read']  : true,
740
			'write'   => isset($this->options['defaults']['write']) ? !!$this->options['defaults']['write'] : true,
741
			'locked'  => isset($this->options['defaults']['locked']) ? !!$this->options['defaults']['locked'] : false,
742
			'hidden'  => isset($this->options['defaults']['hidden']) ? !!$this->options['defaults']['hidden'] : false
743
		);
744
745
		// root attributes
746
		$this->attributes[] = array(
747
			'pattern' => '~^'.preg_quote(DIRECTORY_SEPARATOR).'$~',
748
			'locked'  => true,
749
			'hidden'  => false
750
		);
751
		// set files attributes
752 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...
753
			
754
			foreach ($this->options['attributes'] as $a) {
755
				// attributes must contain pattern and at least one rule
756
				if (!empty($a['pattern']) || count($a) > 1) {
757
					$this->attributes[] = $a;
758
				}
759
			}
760
		}
761
762
		if (!empty($this->options['accessControl']) && is_callable($this->options['accessControl'])) {
763
			$this->access = $this->options['accessControl'];
764
		}
765
		
766
		$this->today     = mktime(0,0,0, date('m'), date('d'), date('Y'));
767
		$this->yesterday = $this->today-86400;
768
		
769
		// 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...
770
		if (!$this->init()) {
771
			return false;
772
		}
773
		
774
		// check some options is arrays
775
		$this->uploadAllow = isset($this->options['uploadAllow']) && is_array($this->options['uploadAllow'])
776
			? $this->options['uploadAllow']
777
			: array();
778
			
779
		$this->uploadDeny = isset($this->options['uploadDeny']) && is_array($this->options['uploadDeny'])
780
			? $this->options['uploadDeny']
781
			: array();
782
783
		if (is_string($this->options['uploadOrder'])) { // telephat_mode on, compatibility with 1.x
784
			$parts = explode(',', isset($this->options['uploadOrder']) ? $this->options['uploadOrder'] : 'deny,allow');
785
			$this->uploadOrder = array(trim($parts[0]), trim($parts[1]));
786
		} else { // telephat_mode off
787
			$this->uploadOrder = $this->options['uploadOrder'];
788
		}
789
			
790
		if (!empty($this->options['uploadMaxSize'])) {
791
			$size = ''.$this->options['uploadMaxSize'];
792
			$unit = strtolower(substr($size, strlen($size) - 1));
793
			$n = 1;
794
			switch ($unit) {
795
				case 'k':
796
					$n = 1024;
797
					break;
798
				case 'm':
799
					$n = 1048576;
800
					break;
801
				case 'g':
802
					$n = 1073741824;
803
			}
804
			$this->uploadMaxSize = intval($size)*$n;
805
		}
806
		// Set maximum to PHP_INT_MAX
807
		if (!defined('PHP_INT_MAX')) {
808
			define('PHP_INT_MAX', 2147483647);
809
		}
810
		if ($this->uploadMaxSize < 1 || $this->uploadMaxSize > PHP_INT_MAX) {
811
			$this->uploadMaxSize = PHP_INT_MAX;
812
		}
813
		
814
		$this->disabled = isset($this->options['disabled']) && is_array($this->options['disabled'])
815
			? $this->options['disabled']
816
			: array();
817
		
818
		$this->cryptLib   = $this->options['cryptLib'];
819
		$this->mimeDetect = $this->options['mimeDetect'];
820
821
		// find available mimetype detect method
822
		$type = strtolower($this->options['mimeDetect']);
823
		$type = preg_match('/^(finfo|mime_content_type|internal|auto)$/i', $type) ? $type : 'auto';
824
		$regexp = '/text\/x\-(php|c\+\+)/';
825
	
826
		if (($type == 'finfo' || $type == 'auto') 
827
		&& class_exists('finfo', false)) {
828
			$tmpFileInfo = @explode(';', @finfo_file(finfo_open(FILEINFO_MIME), __FILE__));
829
		} else {
830
			$tmpFileInfo = false;
831
		}
832
	
833
		if ($tmpFileInfo && preg_match($regexp, array_shift($tmpFileInfo))) {
834
			$type = 'finfo';
835
			$this->finfo = finfo_open(FILEINFO_MIME);
836
		} elseif (($type == 'mime_content_type' || $type == 'auto') 
837
		&& function_exists('mime_content_type')
838
		&& 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...
839
			$type = 'mime_content_type';
840
		} else {
841
			$type = 'internal';
842
		}
843
		$this->mimeDetect = $type;
844
845
		// load mimes from external file for mimeDetect == 'internal'
846
		// based on Alexey Sukhotin idea and patch: http://elrte.org/redmine/issues/163
847
		// file must be in file directory or in parent one 
848
		if ($this->mimeDetect == 'internal' && !self::$mimetypesLoaded) {
849
			self::$mimetypesLoaded = true;
850
			$this->mimeDetect = 'internal';
851
			$file = false;
852
			if (!empty($this->options['mimefile']) && file_exists($this->options['mimefile'])) {
853
				$file = $this->options['mimefile'];
854
			} elseif (file_exists(dirname(__FILE__).DIRECTORY_SEPARATOR.'mime.types')) {
855
				$file = dirname(__FILE__).DIRECTORY_SEPARATOR.'mime.types';
856
			} elseif (file_exists(dirname(dirname(__FILE__)).DIRECTORY_SEPARATOR.'mime.types')) {
857
				$file = dirname(dirname(__FILE__)).DIRECTORY_SEPARATOR.'mime.types';
858
			}
859
860
			if ($file && file_exists($file)) {
861
				$mimecf = file($file);
862
863
				foreach ($mimecf as $line_num => $line) {
864
					if (!preg_match('/^\s*#/', $line)) {
865
						$mime = preg_split('/\s+/', $line, -1, PREG_SPLIT_NO_EMPTY);
866
						for ($i = 1, $size = count($mime); $i < $size ; $i++) {
867
							if (!isset(self::$mimetypes[$mime[$i]])) {
868
								self::$mimetypes[$mime[$i]] = $mime[0];
869
							}
870
						}
871
					}
872
				}
873
			}
874
		}
875
876
		$this->rootName = empty($this->options['alias']) ? $this->basenameCE($this->root) : $this->options['alias'];
877
878
		// This get's triggered if $this->root == '/' and alias is empty.
879
		// Maybe modify _basename instead?
880
		if ($this->rootName === '') $this->rootName = $this->separator;
881
882
		$root = $this->stat($this->root);
883
		
884
		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...
885
			return $this->setError('Root folder does not exists.');
886
		}
887
		if (!$root['read'] && !$root['write']) {
888
			return $this->setError('Root folder has not read and write permissions.');
889
		}
890
		
891
		// 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...
892
		
893
		if ($root['read']) {
894
			// check startPath - path to open by default instead of root
895
			if ($this->options['startPath']) {
896
				$start = $this->stat($this->options['startPath']);
897
				if (!empty($start)
898
				&& $start['mime'] == 'directory'
899
				&& $start['read']
900
				&& empty($start['hidden'])
901
				&& $this->inpathCE($this->options['startPath'], $this->root)) {
902
					$this->startPath = $this->options['startPath'];
903
					if (substr($this->startPath, -1, 1) == $this->options['separator']) {
904
						$this->startPath = substr($this->startPath, 0, -1);
905
					}
906
				}
907
			}
908
		} else {
909
			$this->options['URL']     = '';
910
			$this->options['tmbURL']  = '';
911
			$this->options['tmbPath'] = '';
912
			// read only volume
913
			array_unshift($this->attributes, array(
914
				'pattern' => '/.*/',
915
				'read'    => false
916
			));
917
		}
918
		$this->treeDeep = $this->options['treeDeep'] > 0 ? (int)$this->options['treeDeep'] : 1;
919
		$this->tmbSize  = $this->options['tmbSize'] > 0 ? (int)$this->options['tmbSize'] : 48;
920
		$this->URL      = $this->options['URL'];
921
		if ($this->URL && preg_match("|[^/?&=]$|", $this->URL)) {
922
			$this->URL .= '/';
923
		}
924
925
		$this->tmbURL   = !empty($this->options['tmbURL']) ? $this->options['tmbURL'] : '';
926
		if ($this->tmbURL && preg_match("|[^/?&=]$|", $this->tmbURL)) {
927
			$this->tmbURL .= '/';
928
		}
929
		
930
		$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...
931
			? $this->options['acceptedName']
932
			: '';
933
934
		$this->_checkArchivers();
935
		// manual control archive types to create
936 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...
937
			foreach ($this->archivers['create'] as $mime => $v) {
938
				if (!in_array($mime, $this->options['archiveMimes'])) {
939
					unset($this->archivers['create'][$mime]);
940
				}
941
			}
942
		}
943
		
944
		// manualy add archivers
945 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...
946
			foreach ($this->options['archivers']['create'] as $mime => $conf) {
947
				if (strpos($mime, 'application/') === 0 
948
				&& !empty($conf['cmd']) 
949
				&& isset($conf['argc']) 
950
				&& !empty($conf['ext'])
951
				&& !isset($this->archivers['create'][$mime])) {
952
					$this->archivers['create'][$mime] = $conf;
953
				}
954
			}
955
		}
956
		
957 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...
958
			foreach ($this->options['archivers']['extract'] as $mime => $conf) {
959
				if (strpos($mime, 'application/') === 0
960
				&& !empty($conf['cmd']) 
961
				&& isset($conf['argc']) 
962
				&& !empty($conf['ext'])
963
				&& !isset($this->archivers['extract'][$mime])) {
964
					$this->archivers['extract'][$mime] = $conf;
965
				}
966
			}
967
		}
968
969
		$this->configure();
970
		// 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...
971
		// 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...
972
		return $this->mounted = true;
973
	}
974
	
975
	/**
976
	 * Some "unmount" stuffs - may be required by virtual fs
977
	 *
978
	 * @return void
979
	 * @author Dmitry (dio) Levashov
980
	 **/
981
	public function umount() {
982
	}
983
	
984
	/**
985
	 * Return error message from last failed action
986
	 *
987
	 * @return array
988
	 * @author Dmitry (dio) Levashov
989
	 **/
990
	public function error() {
991
		return $this->error;
992
	}
993
	
994
	/**
995
	 * Return Extention/MIME Table (elFinderVolumeDriver::$mimetypes)
996
	 * 
997
	 * @return array
998
	 * @author Naoki Sawada
999
	 */
1000
	public function getMimeTable() {
1001
		return elFinderVolumeDriver::$mimetypes;
1002
	}
1003
	
1004
	/**
1005
	 * Set mimetypes allowed to display to client
1006
	 *
1007
	 * @param  array  $mimes
1008
	 * @return void
1009
	 * @author Dmitry (dio) Levashov
1010
	 **/
1011
	public function setMimesFilter($mimes) {
1012
		if (is_array($mimes)) {
1013
			$this->onlyMimes = $mimes;
1014
		}
1015
	}
1016
	
1017
	/**
1018
	 * Return root folder hash
1019
	 *
1020
	 * @return string
1021
	 * @author Dmitry (dio) Levashov
1022
	 **/
1023
	public function root() {
1024
		return $this->encode($this->root);
1025
	}
1026
	
1027
	/**
1028
	 * Return target path hash
1029
	 * 
1030
	 * @param  string $path
1031
	 * @param  string $name
1032
	 * @author Naoki Sawada
1033
	 */
1034
	public function getHash($path, $name = '') {
1035
		if ($name !== '') {
1036
			$path = $this->joinPathCE($path, $name);
1037
		}
1038
		return $this->encode($path);
1039
	}
1040
	
1041
	/**
1042
	 * Return root or startPath hash
1043
	 *
1044
	 * @return string
1045
	 * @author Dmitry (dio) Levashov
1046
	 **/
1047
	public function defaultPath() {
1048
		return $this->encode($this->startPath ? $this->startPath : $this->root);
1049
	}
1050
		
1051
	/**
1052
	 * Return volume options required by client:
1053
	 *
1054
	 * @return array
1055
	 * @author Dmitry (dio) Levashov
1056
	 **/
1057
	public function options($hash) {
1058
		$create = $createext = array();
1059
		if (isset($this->archivers['create']) && is_array($this->archivers['create'])) {
1060
			foreach($this->archivers['create'] as $m => $v) {
1061
				$create[] = $m;
1062
				$createext[$m] = $v['ext'];
1063
			}
1064
		}
1065
		return array(
1066
			'path'            => $this->path($hash),
1067
			'url'             => $this->URL,
1068
			'tmbUrl'          => $this->tmbURL,
1069
			'disabled'        => array_merge(array_unique($this->disabled)), // `array_merge` for type array of JSON
1070
			'separator'       => $this->separator,
1071
			'copyOverwrite'   => intval($this->options['copyOverwrite']),
1072
			'uploadOverwrite' => intval($this->options['uploadOverwrite']),
1073
			'uploadMaxSize'   => intval($this->uploadMaxSize),
1074
			'dispInlineRegex' => $this->options['dispInlineRegex'],
1075
			'archivers'       => array(
1076
				'create'    => $create,
1077
				'extract'   => isset($this->archivers['extract']) && is_array($this->archivers['extract']) ? array_keys($this->archivers['extract']) : array(),
1078
				'createext' => $createext
1079
			),
1080
			'uiCmdMap'        => (isset($this->options['uiCmdMap']) && is_array($this->options['uiCmdMap']))? $this->options['uiCmdMap'] : array()
1081
		);
1082
	}
1083
	
1084
	/**
1085
	 * Get option value of this volume
1086
	 * 
1087
	 * @param string $name  target option name
1088
	 * @return NULL|mixed   target option value
1089
	 * @author Naoki Sawada
1090
	 */
1091
	public function getOption($name) {
1092
		return isset($this->options[$name])? $this->options[$name] : null;
1093
	}
1094
	
1095
	/**
1096
	 * Get plugin values of this options
1097
	 * 
1098
	 * @param string $name  Plugin name
1099
	 * @return NULL|array   Plugin values
1100
	 * @author Naoki Sawada
1101
	 */
1102
	public function getOptionsPlugin($name = '') {
1103
		if ($name) {
1104
			return isset($this->options['plugin'][$name])? $this->options['plugin'][$name] : array();
1105
		} else {
1106
			return $this->options['plugin'];
1107
		}
1108
	}
1109
	
1110
	/**
1111
	 * Return true if command disabled in options
1112
	 *
1113
	 * @param  string  $cmd  command name
1114
	 * @return bool
1115
	 * @author Dmitry (dio) Levashov
1116
	 **/
1117
	public function commandDisabled($cmd) {
1118
		return in_array($cmd, $this->disabled);
1119
	}
1120
	
1121
	/**
1122
	 * Return true if mime is required mimes list
1123
	 *
1124
	 * @param  string     $mime   mime type to check
1125
	 * @param  array      $mimes  allowed mime types list or not set to use client mimes list
1126
	 * @param  bool|null  $empty  what to return on empty list
1127
	 * @return bool|null
1128
	 * @author Dmitry (dio) Levashov
1129
	 * @author Troex Nevelin
1130
	 **/
1131
	public function mimeAccepted($mime, $mimes = null, $empty = true) {
1132
		$mimes = is_array($mimes) ? $mimes : $this->onlyMimes;
1133
		if (empty($mimes)) {
1134
			return $empty;
1135
		}
1136
		return $mime == 'directory'
1137
			|| in_array('all', $mimes)
1138
			|| in_array('All', $mimes)
1139
			|| in_array($mime, $mimes)
1140
			|| in_array(substr($mime, 0, strpos($mime, '/')), $mimes);
1141
	}
1142
	
1143
	/**
1144
	 * Return true if voume is readable.
1145
	 *
1146
	 * @return bool
1147
	 * @author Dmitry (dio) Levashov
1148
	 **/
1149
	public function isReadable() {
1150
		$stat = $this->stat($this->root);
1151
		return $stat['read'];
1152
	}
1153
	
1154
	/**
1155
	 * Return true if copy from this volume allowed
1156
	 *
1157
	 * @return bool
1158
	 * @author Dmitry (dio) Levashov
1159
	 **/
1160
	public function copyFromAllowed() {
1161
		return !!$this->options['copyFrom'];
1162
	}
1163
	
1164
	/**
1165
	 * Return file path related to root with convert encoging
1166
	 *
1167
	 * @param  string   $hash  file hash
1168
	 * @return string
1169
	 * @author Dmitry (dio) Levashov
1170
	 **/
1171
	public function path($hash) {
1172
		return $this->convEncOut($this->_path($this->convEncIn($this->decode($hash))));
1173
	}
1174
	
1175
	/**
1176
	 * Return file real path if file exists
1177
	 *
1178
	 * @param  string  $hash  file hash
1179
	 * @return string
1180
	 * @author Dmitry (dio) Levashov
1181
	 **/
1182
	public function realpath($hash) {
1183
		$path = $this->decode($hash);
1184
		return $this->stat($path) ? $path : false;
1185
	}
1186
	
1187
	/**
1188
	 * Return list of moved/overwrited files
1189
	 *
1190
	 * @return array
1191
	 * @author Dmitry (dio) Levashov
1192
	 **/
1193
	public function removed() {
1194
		return $this->removed;
1195
	}
1196
	
1197
	/**
1198
	 * Clean removed files list
1199
	 *
1200
	 * @return void
1201
	 * @author Dmitry (dio) Levashov
1202
	 **/
1203
	public function resetRemoved() {
1204
		$this->removed = array();
1205
	}
1206
	
1207
	/**
1208
	 * Return file/dir hash or first founded child hash with required attr == $val
1209
	 *
1210
	 * @param  string   $hash  file hash
1211
	 * @param  string   $attr  attribute name
1212
	 * @param  bool     $val   attribute value
1213
	 * @return string|false
1214
	 * @author Dmitry (dio) Levashov
1215
	 **/
1216
	public function closest($hash, $attr, $val) {
1217
		return ($path = $this->closestByAttr($this->decode($hash), $attr, $val)) ? $this->encode($path) : false;
1218
	}
1219
	
1220
	/**
1221
	 * Return file info or false on error
1222
	 *
1223
	 * @param  string   $hash      file hash
1224
	 * @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...
1225
	 * @return array|false
1226
	 * @author Dmitry (dio) Levashov
1227
	 **/
1228
	public function file($hash) {
1229
		$path = $this->decode($hash);
1230
		
1231
		$file = $this->stat($path);
1232
		
1233
		if ($hash === $this->root()) {
1234
			$file['uiCmdMap'] = (isset($this->options['uiCmdMap']) && is_array($this->options['uiCmdMap']))? $this->options['uiCmdMap'] : array();
1235
			$file['disabled'] = array_merge(array_unique($this->disabled)); // `array_merge` for type array of JSON
1236
		}
1237
		
1238
		return ($file) ? $file : $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1239
	}
1240
	
1241
	/**
1242
	 * Return folder info
1243
	 *
1244
	 * @param  string   $hash  folder hash
1245
	 * @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...
1246
	 * @return array|false
1247
	 * @author Dmitry (dio) Levashov
1248
	 **/
1249
	public function dir($hash, $resolveLink=false) {
1250
		if (($dir = $this->file($hash)) == false) {
1251
			return $this->setError(elFinder::ERROR_DIR_NOT_FOUND);
1252
		}
1253
1254
		if ($resolveLink && !empty($dir['thash'])) {
1255
			$dir = $this->file($dir['thash']);
1256
		}
1257
		
1258
		return $dir && $dir['mime'] == 'directory' && empty($dir['hidden']) 
1259
			? $dir 
1260
			: $this->setError(elFinder::ERROR_NOT_DIR);
1261
	}
1262
	
1263
	/**
1264
	 * Return directory content or false on error
1265
	 *
1266
	 * @param  string   $hash   file hash
1267
	 * @return array|false
1268
	 * @author Dmitry (dio) Levashov
1269
	 **/
1270
	public function scandir($hash) {
1271
		if (($dir = $this->dir($hash)) == false) {
1272
			return false;
1273
		}
1274
		
1275
		return $dir['read']
1276
			? $this->getScandir($this->decode($hash))
1277
			: $this->setError(elFinder::ERROR_PERM_DENIED);
1278
	}
1279
1280
	/**
1281
	 * Return dir files names list
1282
	 * 
1283
	 * @param  string  $hash   file hash
1284
	 * @return array
1285
	 * @author Dmitry (dio) Levashov
1286
	 **/
1287
	public function ls($hash) {
1288 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...
1289
			return false;
1290
		}
1291
		
1292
		$list = array();
1293
		$path = $this->decode($hash);
1294
		
1295
		foreach ($this->getScandir($path) as $stat) {
1296
			if (empty($stat['hidden']) && $this->mimeAccepted($stat['mime'])) {
1297
				$list[] = $stat['name'];
1298
			}
1299
		}
1300
1301
		return $list;
1302
	}
1303
1304
	/**
1305
	 * Return subfolders for required folder or false on error
1306
	 *
1307
	 * @param  string   $hash  folder hash or empty string to get tree from root folder
1308
	 * @param  int      $deep  subdir deep
1309
	 * @param  string   $exclude  dir hash which subfolders must be exluded from result, required to not get stat twice on cwd subfolders
1310
	 * @return array|false
1311
	 * @author Dmitry (dio) Levashov
1312
	 **/
1313
	public function tree($hash='', $deep=0, $exclude='') {
1314
		$path = $hash ? $this->decode($hash) : $this->root;
1315
		
1316 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...
1317
			return false;
1318
		}
1319
		
1320
		$dirs = $this->gettree($path, $deep > 0 ? $deep -1 : $this->treeDeep-1, $exclude ? $this->decode($exclude) : null);
1321
		array_unshift($dirs, $dir);
1322
		return $dirs;
1323
	}
1324
	
1325
	/**
1326
	 * Return part of dirs tree from required dir up to root dir
1327
	 *
1328
	 * @param  string    $hash   directory hash
1329
	 * @param  bool|null $lineal only lineal parents
1330
	 * @return array
1331
	 * @author Dmitry (dio) Levashov
1332
	 **/
1333
	public function parents($hash, $lineal = false) {
1334
		if (($current = $this->dir($hash)) == false) {
1335
			return false;
1336
		}
1337
1338
		$path = $this->decode($hash);
1339
		$tree = array();
1340
		
1341
		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...
1342
			$path = $this->dirnameCE($path);
1343
			$stat = $this->stat($path);
1344
			if (!empty($stat['hidden']) || !$stat['read']) {
1345
				return false;
1346
			}
1347
			
1348
			array_unshift($tree, $stat);
1349
			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...
1350
				foreach ($this->gettree($path, 0) as $dir) {
1351
					if (!in_array($dir, $tree)) {
1352
						$tree[] = $dir;
1353
					}
1354
				}
1355
			}
1356
		}
1357
1358
		return $tree ? $tree : array($current);
1359
	}
1360
	
1361
	/**
1362
	 * Create thumbnail for required file and return its name of false on failed
1363
	 *
1364
	 * @return string|false
1365
	 * @author Dmitry (dio) Levashov
1366
	 **/
1367
	public function tmb($hash) {
1368
		$path = $this->decode($hash);
1369
		$stat = $this->stat($path);
1370
		
1371
		if (isset($stat['tmb'])) {
1372
			return $stat['tmb'] == "1" ? $this->createTmb($path, $stat) : $stat['tmb'];
1373
		}
1374
		return false;
1375
	}
1376
	
1377
	/**
1378
	 * Return file size / total directory size
1379
	 *
1380
	 * @param  string   file hash
1381
	 * @return int
1382
	 * @author Dmitry (dio) Levashov
1383
	 **/
1384
	public function size($hash) {
1385
		return $this->countSize($this->decode($hash));
1386
	}
1387
	
1388
	/**
1389
	 * Open file for reading and return file pointer
1390
	 *
1391
	 * @param  string   file hash
1392
	 * @return Resource
1393
	 * @author Dmitry (dio) Levashov
1394
	 **/
1395
	public function open($hash) {
1396 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...
1397
		|| $file['mime'] == 'directory') {
1398
			return false;
1399
		}
1400
		
1401
		return $this->fopenCE($this->decode($hash), 'rb');
1402
	}
1403
	
1404
	/**
1405
	 * Close file pointer
1406
	 *
1407
	 * @param  Resource  $fp   file pointer
1408
	 * @param  string    $hash file hash
1409
	 * @return void
1410
	 * @author Dmitry (dio) Levashov
1411
	 **/
1412
	public function close($fp, $hash) {
1413
		$this->fcloseCE($fp, $this->decode($hash));
1414
	}
1415
	
1416
	/**
1417
	 * Create directory and return dir info
1418
	 *
1419
	 * @param  string   $dsthash  destination directory hash
1420
	 * @param  string   $name directory name
1421
	 * @return array|false
1422
	 * @author Dmitry (dio) Levashov
1423
	 **/
1424
	public function mkdir($dsthash, $name) {
1425
		if ($this->commandDisabled('mkdir')) {
1426
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1427
		}
1428
		
1429
		if (!$this->nameAccepted($name)) {
1430
			return $this->setError(elFinder::ERROR_INVALID_NAME);
1431
		}
1432
		
1433
		if (($dir = $this->dir($dsthash)) == false) {
1434
			return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dsthash);
1435
		}
1436
		
1437
		$path = $this->decode($dsthash);
1438
		
1439
		if (!$dir['write'] || !$this->allowCreate($path, $name, true)) {
1440
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1441
		}
1442
		
1443
		$dst  = $this->joinPathCE($path, $name);
1444
		$stat = $this->stat($dst); 
1445
		if (!empty($stat)) { 
1446
			return $this->setError(elFinder::ERROR_EXISTS, $name);
1447
		}
1448
		$this->clearcache();
1449
		return ($path = $this->convEncOut($this->_mkdir($this->convEncIn($path), $this->convEncIn($name)))) ? $this->stat($path) : false;
1450
	}
1451
	
1452
	/**
1453
	 * Create empty file and return its info
1454
	 *
1455
	 * @param  string   $dst  destination directory
1456
	 * @param  string   $name file name
1457
	 * @return array|false
1458
	 * @author Dmitry (dio) Levashov
1459
	 **/
1460
	public function mkfile($dst, $name) {
1461
		if ($this->commandDisabled('mkfile')) {
1462
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1463
		}
1464
		
1465
		if (!$this->nameAccepted($name)) {
1466
			return $this->setError(elFinder::ERROR_INVALID_NAME);
1467
		}
1468
		
1469 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...
1470
			return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst);
1471
		}
1472
		
1473
		$path = $this->decode($dst);
1474
		
1475
		if (!$dir['write'] || !$this->allowCreate($path, $name, false)) {
1476
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1477
		}
1478
		
1479
		if ($this->stat($this->joinPathCE($path, $name))) {
1480
			return $this->setError(elFinder::ERROR_EXISTS, $name);
1481
		}
1482
		
1483
		$this->clearcache();
1484
		return ($path = $this->convEncOut($this->_mkfile($this->convEncIn($path), $this->convEncIn($name)))) ? $this->stat($path) : false;
1485
	}
1486
	
1487
	/**
1488
	 * Rename file and return file info
1489
	 *
1490
	 * @param  string  $hash  file hash
1491
	 * @param  string  $name  new file name
1492
	 * @return array|false
1493
	 * @author Dmitry (dio) Levashov
1494
	 **/
1495
	public function rename($hash, $name) {
1496
		if ($this->commandDisabled('rename')) {
1497
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1498
		}
1499
		
1500
		if (!$this->nameAccepted($name)) {
1501
			return $this->setError(elFinder::ERROR_INVALID_NAME, $name);
1502
		}
1503
		
1504
		$mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name);
1505 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...
1506
			return $this->setError(elFinder::ERROR_INVALID_NAME, $name);
1507
		}
1508
		
1509
		if (!($file = $this->file($hash))) {
1510
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1511
		}
1512
		
1513
		if ($name == $file['name']) {
1514
			return $file;
1515
		}
1516
		
1517
		if (!empty($file['locked'])) {
1518
			return $this->setError(elFinder::ERROR_LOCKED, $file['name']);
1519
		}
1520
		
1521
		$path = $this->decode($hash);
1522
		$dir  = $this->dirnameCE($path);
1523
		$stat = $this->stat($this->joinPathCE($dir, $name));
1524
		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...
1525
			return $this->setError(elFinder::ERROR_EXISTS, $name);
1526
		}
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
		$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...
1533
1534
1535 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...
1536
			$this->clearcache();
1537
			return $this->stat($path);
1538
		}
1539
		return false;
1540
	}
1541
	
1542
	/**
1543
	 * Create file copy with suffix "copy number" and return its info
1544
	 *
1545
	 * @param  string   $hash    file hash
1546
	 * @param  string   $suffix  suffix to add to file name
1547
	 * @return array|false
1548
	 * @author Dmitry (dio) Levashov
1549
	 **/
1550
	public function duplicate($hash, $suffix='copy') {
1551
		if ($this->commandDisabled('duplicate')) {
1552
			return $this->setError(elFinder::ERROR_COPY, '#'.$hash, elFinder::ERROR_PERM_DENIED);
1553
		}
1554
		
1555
		if (($file = $this->file($hash)) == false) {
1556
			return $this->setError(elFinder::ERROR_COPY, elFinder::ERROR_FILE_NOT_FOUND);
1557
		}
1558
1559
		$path = $this->decode($hash);
1560
		$dir  = $this->dirnameCE($path);
1561
		$name = $this->uniqueName($dir, $this->basenameCE($path), ' '.$suffix.' ');
1562
1563 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...
1564
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1565
		}
1566
1567
		return ($path = $this->copy($path, $dir, $name)) == false
1568
			? false
1569
			: $this->stat($path);
0 ignored issues
show
Bug introduced by
It seems like $path defined by $this->copy($path, $dir, $name) on line 1567 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...
1570
	}
1571
	
1572
	/**
1573
	 * Save uploaded file. 
1574
	 * On success return array with new file stat and with removed file hash (if existed file was replaced)
1575
	 *
1576
	 * @param  Resource $fp      file pointer
1577
	 * @param  string   $dst     destination folder hash
1578
	 * @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...
1579
	 * @param  string   $tmpname file tmp name - required to detect mime type
1580
	 * @return array|false
1581
	 * @author Dmitry (dio) Levashov
1582
	 **/
1583
	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...
1584
		if ($this->commandDisabled('upload')) {
1585
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1586
		}
1587
		
1588 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...
1589
			return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst);
1590
		}
1591
1592
		if (!$dir['write']) {
1593
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1594
		}
1595
		
1596
		if (!$this->nameAccepted($name)) {
1597
			return $this->setError(elFinder::ERROR_INVALID_NAME);
1598
		}
1599
		
1600
		$mime = $this->mimetype($this->mimeDetect == 'internal' ? $name : $tmpname, $name);
1601
		$mimeByName = '';
1602
		if ($this->mimeDetect !== 'internal') {
1603
			$mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name);
1604
			if ($mime == 'unknown') {
1605
				$mime = $mimeByName;
1606
			}
1607
		}
1608
1609 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...
1610
			return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME);
1611
		}
1612
1613
		$tmpsize = sprintf('%u', filesize($tmpname));
1614
		if ($this->uploadMaxSize > 0 && $tmpsize > $this->uploadMaxSize) {
1615
			return $this->setError(elFinder::ERROR_UPLOAD_FILE_SIZE);
1616
		}
1617
1618
		$dstpath = $this->decode($dst);
1619
		$test    = $this->joinPathCE($dstpath, $name);
1620
		
1621
		$file = $this->stat($test);
1622
		$this->clearcache();
1623
		
1624
		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...
1625
			// check POST data `overwrite` for 3rd party uploader
1626
			$overwrite = isset($_POST['overwrite'])? (bool)$_POST['overwrite'] : $this->options['uploadOverwrite'];
1627
			if ($overwrite) {
1628 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...
1629
					return $this->setError(elFinder::ERROR_PERM_DENIED);
1630
				} elseif ($file['mime'] == 'directory') {
1631
					return $this->setError(elFinder::ERROR_NOT_REPLACE, $name);
1632
				} 
1633
				$this->remove($test);
1634
			} else {
1635
				$name = $this->uniqueName($dstpath, $name, '-', false);
1636
			}
1637
		}
1638
		
1639
		$stat = array(
1640
			'mime'   => $mime, 
1641
			'width'  => 0, 
1642
			'height' => 0, 
1643
			'size'   => $tmpsize);
1644
		
1645
		// $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...
1646
		if (strpos($mime, 'image') === 0 && ($s = getimagesize($tmpname))) {
1647
			$stat['width'] = $s[0];
1648
			$stat['height'] = $s[1];
1649
		}
1650
		// $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...
1651
		if (($path = $this->saveCE($fp, $dstpath, $name, $stat)) == false) {
1652
			return false;
1653
		}
1654
		
1655
		
1656
1657
		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 1651 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...
1658
	}
1659
	
1660
	/**
1661
	 * Paste files
1662
	 *
1663
	 * @param  Object  $volume  source volume
1664
	 * @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...
1665
	 * @param  string  $dst     destination dir hash
1666
	 * @param  bool    $rmSrc   remove source after copy?
1667
	 * @return array|false
1668
	 * @author Dmitry (dio) Levashov
1669
	 **/
1670
	public function paste($volume, $src, $dst, $rmSrc = false) {
1671
		$err = $rmSrc ? elFinder::ERROR_MOVE : elFinder::ERROR_COPY;
1672
		
1673
		if ($this->commandDisabled('paste')) {
1674
			return $this->setError($err, '#'.$src, elFinder::ERROR_PERM_DENIED);
1675
		}
1676
1677
		if (($file = $volume->file($src, $rmSrc)) == false) {
1678
			return $this->setError($err, '#'.$src, elFinder::ERROR_FILE_NOT_FOUND);
1679
		}
1680
1681
		$name = $file['name'];
1682
		$errpath = $volume->path($file['hash']);
1683
		
1684 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...
1685
			return $this->setError($err, $errpath, elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst);
1686
		}
1687
		
1688
		if (!$dir['write'] || !$file['read']) {
1689
			return $this->setError($err, $errpath, elFinder::ERROR_PERM_DENIED);
1690
		}
1691
1692
		$destination = $this->decode($dst);
1693
1694
		if (($test = $volume->closest($src, $rmSrc ? 'locked' : 'read', $rmSrc))) {
1695
			return $rmSrc
1696
				? $this->setError($err, $errpath, elFinder::ERROR_LOCKED, $volume->path($test))
1697
				: $this->setError($err, $errpath, !empty($file['thash'])? elFinder::ERROR_PERM_DENIED : elFinder::ERROR_MKOUTLINK);
1698
		}
1699
1700
		$test = $this->joinPathCE($destination, $name);
1701
		$stat = $this->stat($test);
1702
		$this->clearcache();
1703
		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...
1704
			if ($this->options['copyOverwrite']) {
1705
				// do not replace file with dir or dir with file
1706
				if (!$this->isSameType($file['mime'], $stat['mime'])) {
1707
					return $this->setError(elFinder::ERROR_NOT_REPLACE, $this->path($stat['hash']));
1708
				}
1709
				// existed file is not writable
1710
				if (!$stat['write']) {
1711
					return $this->setError($err, $errpath, elFinder::ERROR_PERM_DENIED);
1712
				}
1713
				// existed file locked or has locked child
1714
				if (($locked = $this->closestByAttr($test, 'locked', true))) {
1715
					$stat = $this->stat($locked);
1716
					return $this->setError(elFinder::ERROR_LOCKED, $this->path($stat['hash']));
1717
				}
1718
				// target is entity file of alias
1719
				if ($volume == $this && ($test == @$file['target'] || $test == $this->decode($src))) {
1720
					return $this->setError(elFinder::ERROR_REPLACE, $errpath);
1721
				}
1722
				// remove existed file
1723
				if (!$this->remove($test)) {
1724
					return $this->setError(elFinder::ERROR_REPLACE, $this->path($stat['hash']));
1725
				}
1726
			} else {
1727
				$name = $this->uniqueName($destination, $name, ' ', false);
1728
			}
1729
		}
1730
		
1731
		// copy/move inside current volume
1732
		if ($volume == $this) {
1733
			$source = $this->decode($src);
1734
			// do not copy into itself
1735
			if ($this->inpathCE($destination, $source)) {
1736
				return $this->setError(elFinder::ERROR_COPY_INTO_ITSELF, $errpath);
1737
			}
1738
			$method = $rmSrc ? 'move' : 'copy';
1739
			return ($path = $this->$method($source, $destination, $name)) ? $this->stat($path) : false;
1740
		}
1741
		
1742
		// copy/move from another volume
1743
		if (!$this->options['copyTo'] || !$volume->copyFromAllowed()) {
1744
			return $this->setError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_PERM_DENIED);
1745
		}
1746
		
1747
		if (($path = $this->copyFrom($volume, $src, $destination, $name)) == false) {
1748
			return false;
1749
		}
1750
		
1751
		if ($rmSrc) {
1752
			if (!$volume->rm($src)) {
1753
				return $this->setError(elFinder::ERROR_MOVE, $errpath, elFinder::ERROR_RM_SRC);
1754
			}
1755
		}
1756
		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 1747 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...
1757
	}
1758
	
1759
	/**
1760
	 * Return file contents
1761
	 *
1762
	 * @param  string  $hash  file hash
1763
	 * @return string|false
1764
	 * @author Dmitry (dio) Levashov
1765
	 **/
1766
	public function getContents($hash) {
1767
		$file = $this->file($hash);
1768
		
1769
		if (!$file) {
1770
			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...
1771
		}
1772
		
1773
		if ($file['mime'] == 'directory') {
1774
			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...
1775
		}
1776
		
1777
		if (!$file['read']) {
1778
			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...
1779
		}
1780
		
1781
		return $this->convEncOut($this->_getContents($this->convEncIn($this->decode($hash))));
1782
	}
1783
	
1784
	/**
1785
	 * Put content in text file and return file info.
1786
	 *
1787
	 * @param  string  $hash     file hash
1788
	 * @param  string  $content  new file content
1789
	 * @return array
1790
	 * @author Dmitry (dio) Levashov
1791
	 **/
1792
	public function putContents($hash, $content) {
1793
		if ($this->commandDisabled('edit')) {
1794
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1795
		}
1796
		
1797
		$path = $this->decode($hash);
1798
		
1799
		if (!($file = $this->file($hash))) {
1800
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1801
		}
1802
		
1803
		if (!$file['write']) {
1804
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1805
		}
1806
		
1807
		// check MIME
1808
		$name = $this->basenameCE($path);
1809
		$mime = '';
1810
		$mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name);
1811
		if ($this->mimeDetect !== 'internal') {
1812
			if ($tp = tmpfile()) {
1813
				fwrite($tp, $content);
1814
				$info = stream_get_meta_data($tp);
1815
				$filepath = $info['uri'];
1816
				$mime = $this->mimetype($filepath, $name);
1817
				fclose($tp);
1818
			}
1819
		}
1820 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...
1821
			return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME);
1822
		}
1823
		
1824
		$this->clearcache();
1825
		return $this->convEncOut($this->_filePutContents($this->convEncIn($path), $content)) ? $this->stat($path) : false;
1826
	}
1827
	
1828
	/**
1829
	 * Extract files from archive
1830
	 *
1831
	 * @param  string  $hash  archive hash
1832
	 * @return array|bool
1833
	 * @author Dmitry (dio) Levashov, 
1834
	 * @author Alexey Sukhotin
1835
	 **/
1836
	public function extract($hash, $makedir = null) {
1837
		if ($this->commandDisabled('extract')) {
1838
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1839
		}
1840
		
1841
		if (($file = $this->file($hash)) == false) {
1842
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1843
		}
1844
		
1845
		$archiver = isset($this->archivers['extract'][$file['mime']])
1846
			? $this->archivers['extract'][$file['mime']]
1847
			: false;
1848
			
1849
		if (!$archiver) {
1850
			return $this->setError(elFinder::ERROR_NOT_ARCHIVE);
1851
		}
1852
		
1853
		$path   = $this->decode($hash);
1854
		$parent = $this->stat($this->dirnameCE($path));
1855
1856
		if (!$file['read'] || !$parent['write']) {
1857
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1858
		}
1859
		$this->clearcache();
1860
		$this->extractToNewdir = is_null($makedir)? 'auto' : (bool)$makedir;
0 ignored issues
show
Documentation Bug introduced by
It seems like is_null($makedir) ? 'auto' : (bool) $makedir of type string or boolean is incompatible with the declared type integer of property $extractToNewdir.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
1861
		
1862
		if ($path = $this->convEncOut($this->_extract($this->convEncIn($path), $archiver))) {
1863
			if (is_array($path)) {
1864
				foreach ($path as $_k => $_p) {
1865
					$path[$_k] = $this->stat($_p);
1866
				}
1867
			} else {
1868
				$path = $this->stat($path);
1869
			}
1870
			return $path;
1871
		} else {
1872
			return false;
1873
		}
1874
	}
1875
1876
	/**
1877
	 * Add files to archive
1878
	 *
1879
	 * @return void
1880
	 **/
1881
	public function archive($hashes, $mime, $name = '') {
1882
		if ($this->commandDisabled('archive')) {
1883
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1884
		}
1885
1886
		$archiver = isset($this->archivers['create'][$mime])
1887
			? $this->archivers['create'][$mime]
1888
			: false;
1889
			
1890
		if (!$archiver) {
1891
			return $this->setError(elFinder::ERROR_ARCHIVE_TYPE);
1892
		}
1893
		
1894
		$files = array();
1895
		
1896
		foreach ($hashes as $hash) {
1897
			if (($file = $this->file($hash)) == false) {
1898
				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...
1899
			}
1900
			if (!$file['read']) {
1901
				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...
1902
			}
1903
			$path = $this->decode($hash);
1904
			if (!isset($dir)) {
1905
				$dir = $this->dirnameCE($path);
1906
				$stat = $this->stat($dir);
1907
				if (!$stat['write']) {
1908
					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...
1909
				}
1910
			}
1911
			
1912
			$files[] = $this->basenameCE($path);
1913
		}
1914
		
1915
		if ($name === '') {
1916
			$name = count($files) == 1 ? $files[0] : 'Archive';
1917
		} else {
1918
			$name = str_replace(array('/', '\\'), '_', preg_replace('/\.' . preg_quote($archiver['ext'], '/') . '$/i', '', $name));
1919
		}
1920
		$name .='.' . $archiver['ext'];
1921
		$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...
1922
		$this->clearcache();
1923
		return ($path = $this->convEncOut($this->_archive($this->convEncIn($dir), $this->convEncIn($files), $this->convEncIn($name), $archiver))) ? $this->stat($path) : false;
1924
	}
1925
	
1926
	/**
1927
	 * Resize image
1928
	 *
1929
	 * @param  string   $hash    image file
1930
	 * @param  int      $width   new width
1931
	 * @param  int      $height  new height
1932
	 * @param  int      $x       X start poistion for crop
1933
	 * @param  int      $y       Y start poistion for crop
1934
	 * @param  string   $mode    action how to mainpulate image
1935
	 * @return array|false
1936
	 * @author Dmitry (dio) Levashov
1937
	 * @author Alexey Sukhotin
1938
	 * @author nao-pon
1939
	 * @author Troex Nevelin
1940
	 **/
1941
	public function resize($hash, $width, $height, $x, $y, $mode = 'resize', $bg = '', $degree = 0) {
1942
		if ($this->commandDisabled('resize')) {
1943
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1944
		}
1945
		
1946
		if (($file = $this->file($hash)) == false) {
1947
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1948
		}
1949
		
1950
		if (!$file['write'] || !$file['read']) {
1951
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1952
		}
1953
		
1954
		$path = $this->decode($hash);
1955
		
1956
		$work_path = $this->getWorkFile($this->encoding? $this->convEncIn($path, true) : $path);
1957
1958
		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...
1959
			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...
1960
				@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...
1961
			}
1962
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1963
		}
1964
1965
		if ($this->imgLib != 'imagick') {
1966
			if (elFinder::isAnimationGif($work_path)) {
1967
				return $this->setError(elFinder::ERROR_UNSUPPORT_TYPE);
1968
			}
1969
		}
1970
1971
		switch($mode) {
1972
			
1973
			case 'propresize':
1974
				$result = $this->imgResize($work_path, $width, $height, true, true);
1975
				break;
1976
1977
			case 'crop':
1978
				$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...
1979
				break;
1980
1981
			case 'fitsquare':
1982
				$result = $this->imgSquareFit($work_path, $width, $height, 'center', 'middle', ($bg ? $bg : $this->options['tmbBgColor']));
1983
				break;
1984
1985
			case 'rotate':
1986
				$result = $this->imgRotate($work_path, $degree, ($bg ? $bg : $this->options['tmbBgColor']));
1987
				break;
1988
1989
			default:
1990
				$result = $this->imgResize($work_path, $width, $height, false, true);
1991
				break;
1992
		}
1993
		
1994
		$ret = false;
1995
		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...
1996
			$stat = $this->stat($path);
1997
			clearstatcache();
1998
			$fstat = stat($work_path);
1999
			$stat['size'] = $fstat['size'];
2000
			$stat['ts'] = $fstat['mtime'];
2001
			if ($imgsize = @getimagesize($work_path)) {
2002
				$stat['width'] = $imgsize[0];
2003
				$stat['height'] = $imgsize[1];
2004
				$stat['mime'] = $imgsize['mime'];
2005
			}
2006
			if ($path !== $work_path) {
2007
				if ($fp = @fopen($work_path, 'rb')) {
2008
					$ret = $this->saveCE($fp, $this->dirnameCE($path), $this->basenameCE($path), $stat);
2009
					@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...
2010
				}
2011
			} else {
2012
				$ret = true;
2013
			}
2014
			if ($ret) {
2015
				$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...
2016
				$this->clearcache();
2017
				$ret = $this->stat($path);
2018
				$ret['width'] = $stat['width'];
2019
				$ret['height'] = $stat['height'];
2020
			}
2021
		}
2022
		if ($path !== $work_path) {
2023
			is_file($work_path) && @unlink($work_path);
2024
		}
2025
		
2026
		return $ret;
2027
	}
2028
	
2029
	/**
2030
	 * Remove file/dir
2031
	 *
2032
	 * @param  string  $hash  file hash
2033
	 * @return bool
2034
	 * @author Dmitry (dio) Levashov
2035
	 **/
2036
	public function rm($hash) {
2037
		return $this->commandDisabled('rm')
2038
			? $this->setError(elFinder::ERROR_PERM_DENIED)
2039
			: $this->remove($this->decode($hash));
2040
	}
2041
	
2042
	/**
2043
	 * Search files
2044
	 *
2045
	 * @param  string  $q  search string
2046
	 * @param  array   $mimes
2047
	 * @return array
2048
	 * @author Dmitry (dio) Levashov
2049
	 **/
2050
	public function search($q, $mimes, $hash = null) {
2051
		$dir = null;
2052
		if ($hash) {
2053
			$dir = $this->decode($hash);
2054
			$stat = $this->stat($dir);
2055
			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...
2056
				$q = '';
2057
			}
2058
		}
2059
		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...
2060
			$mimes = array_intersect($mimes, $this->onlyMimes);
2061
			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...
2062
				$q = '';
2063
			}
2064
		}
2065
		return ($q === '' || $this->commandDisabled('search'))
2066
			? array()
2067
			: $this->doSearch(is_null($dir)? $this->root : $dir, $q, $mimes);
2068
	}
2069
	
2070
	/**
2071
	 * Return image dimensions
2072
	 *
2073
	 * @param  string  $hash  file hash
2074
	 * @return array
2075
	 * @author Dmitry (dio) Levashov
2076
	 **/
2077
	public function dimensions($hash) {
2078
		if (($file = $this->file($hash)) == false) {
2079
			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...
2080
		}
2081
		
2082
		return $this->convEncOut($this->_dimensions($this->convEncIn($this->decode($hash)), $file['mime']));
2083
	}
2084
	
2085
	/**
2086
	 * Return content URL (for netmout volume driver)
2087
	 * If file.url == 1 requests from JavaScript client with XHR
2088
	 * 
2089
	 * @param string $hash  file hash
2090
	 * @param array $options  options array
2091
	 * @return boolean|string
2092
	 * @author Naoki Sawada
2093
	 */
2094
	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...
2095 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...
2096
			return false;
2097
		}
2098
		return $file['url'];
2099
	}
2100
	
2101
	/**
2102
	 * Return temp path
2103
	 * 
2104
	 * @return string
2105
	 * @author Naoki Sawada
2106
	 */
2107
	public function getTempPath() {
2108
		if (@ $this->tmpPath) {
2109
			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...
2110
		} else if (@ $this->tmp) {
2111
			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...
2112
		} else if (function_exists('sys_get_temp_dir')) {
2113
			return sys_get_temp_dir();
2114
		} else if (@ $this->tmbPath) {
2115
			return $this->tmbPath;
2116
		} else {
2117
			return null;
2118
		}
2119
	}
2120
	
2121
	/**
2122
	 * (Make &) Get upload taget dirctory hash
2123
	 * 
2124
	 * @param string $baseTargetHash
2125
	 * @param string $path
2126
	 * @param array  $result
2127
	 * @return boolean|string
2128
	 * @author Naoki Sawada
2129
	 */
2130
	public function getUploadTaget($baseTargetHash, $path, & $result) {
2131
		$base = $this->decode($baseTargetHash);
2132
		$targetHash = $baseTargetHash;
2133
		$path = ltrim($path, $this->separator);
2134
		$dirs = explode($this->separator, $path);
2135
		array_pop($dirs);
2136
		foreach($dirs as $dir) {
2137
			$targetPath = $this->joinPathCE($base, $dir);
2138
			if (! $_realpath = $this->realpath($this->encode($targetPath))) {
2139
				if ($stat = $this->mkdir($targetHash, $dir)) {
2140
					$result['added'][] = $stat;
2141
					$targetHash = $stat['hash'];
2142
					$base = $this->decode($targetHash);
2143
				} else {
2144
					return false;
2145
				}
2146
			} else {
2147
				$targetHash = $this->encode($_realpath);
2148
				if ($this->dir($targetHash)) {
2149
					$base = $this->decode($targetHash);
2150
				} else {
2151
					return false;
2152
				}
2153
			}
2154
		}
2155
		return $targetHash;
2156
	}
2157
	
2158
	/**
2159
	 * Return this uploadMaxSize value
2160
	 * 
2161
	 * @return integer
2162
	 * @author Naoki Sawada
2163
	 */
2164
	public function getUploadMaxSize() {
2165
		return $this->uploadMaxSize;
2166
	}
2167
	
2168
	/**
2169
	 * Save error message
2170
	 *
2171
	 * @param  array  error 
2172
	 * @return false
2173
	 * @author Dmitry(dio) Levashov
2174
	 **/
2175
	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...
2176
		
2177
		$this->error = array();
2178
		
2179
		foreach (func_get_args() as $err) {
2180
			if (is_array($err)) {
2181
				$this->error = array_merge($this->error, $err);
2182
			} else {
2183
				$this->error[] = $err;
2184
			}
2185
		}
2186
		
2187
		// $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...
2188
		return false;
2189
	}
2190
	
2191
	/*********************************************************************/
2192
	/*                               FS API                              */
2193
	/*********************************************************************/
2194
	
2195
	/***************** server encoding support *******************/
2196
	
2197
	/**
2198
	 * Return parent directory path (with convert encording)
2199
	 *
2200
	 * @param  string  $path  file path
2201
	 * @return string
2202
	 * @author Naoki Sawada
2203
	 **/
2204
	protected function dirnameCE($path) {
2205
		return (!$this->encoding)? $this->_dirname($path) :	$this->convEncOut($this->_dirname($this->convEncIn($path)));
2206
	}
2207
	
2208
	/**
2209
	 * Return file name (with convert encording)
2210
	 *
2211
	 * @param  string  $path  file path
2212
	 * @return string
2213
	 * @author Naoki Sawada
2214
	 **/
2215
	protected function basenameCE($path) {
2216
		return (!$this->encoding)? $this->_basename($path) : $this->convEncOut($this->_basename($this->convEncIn($path)));
2217
	}
2218
	
2219
	/**
2220
	 * Join dir name and file name and return full path. (with convert encording)
2221
	 * Some drivers (db) use int as path - so we give to concat path to driver itself
2222
	 *
2223
	 * @param  string  $dir   dir path
2224
	 * @param  string  $name  file name
2225
	 * @return string
2226
	 * @author Naoki Sawada
2227
	 **/
2228
	protected function joinPathCE($dir, $name) {
2229
		return (!$this->encoding)? $this->_joinPath($dir, $name) : $this->convEncOut($this->_joinPath($this->convEncIn($dir), $this->convEncIn($name)));
2230
	}
2231
	
2232
	/**
2233
	 * Return normalized path (with convert encording)
2234
	 *
2235
	 * @param  string  $path  file path
2236
	 * @return string
2237
	 * @author Naoki Sawada
2238
	 **/
2239
	protected function normpathCE($path) {
2240
		return (!$this->encoding)? $this->_normpath($path) : $this->convEncOut($this->_normpath($this->convEncIn($path)));
2241
	}
2242
	
2243
	/**
2244
	 * Return file path related to root dir (with convert encording)
2245
	 *
2246
	 * @param  string  $path  file path
2247
	 * @return string
2248
	 * @author Naoki Sawada
2249
	 **/
2250
	protected function relpathCE($path) {
2251
		return (!$this->encoding)? $this->_relpath($path) : $this->convEncOut($this->_relpath($this->convEncIn($path)));
2252
	}
2253
	
2254
	/**
2255
	 * Convert path related to root dir into real path (with convert encording)
2256
	 *
2257
	 * @param  string  $path  rel file path
2258
	 * @return string
2259
	 * @author Naoki Sawada
2260
	 **/
2261
	protected function abspathCE($path) {
2262
		return (!$this->encoding)? $this->_abspath($path): $this->convEncOut($this->_abspath($this->convEncIn($path)));
2263
	}
2264
	
2265
	/**
2266
	 * Return true if $path is children of $parent (with convert encording)
2267
	 *
2268
	 * @param  string  $path    path to check
2269
	 * @param  string  $parent  parent path
2270
	 * @return bool
2271
	 * @author Naoki Sawada
2272
	 **/
2273
	protected function inpathCE($path, $parent) {
2274
		return (!$this->encoding)? $this->_inpath($path, $parent) : $this->convEncOut($this->_inpath($this->convEncIn($path), $this->convEncIn($parent)));
2275
	}
2276
	
2277
	/**
2278
	 * Open file and return file pointer (with convert encording)
2279
	 *
2280
	 * @param  string  $path  file path
2281
	 * @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...
2282
	 * @return resource|false
2283
	 * @author Naoki Sawada
2284
	 **/
2285
	protected function fopenCE($path, $mode='rb') {
2286
		return (!$this->encoding)? $this->_fopen($path, $mode) : $this->convEncOut($this->_fopen($this->convEncIn($path), $mode));
2287
	}
2288
	
2289
	/**
2290
	 * Close opened file (with convert encording)
2291
	 * 
2292
	 * @param  resource  $fp    file pointer
2293
	 * @param  string    $path  file path
2294
	 * @return bool
2295
	 * @author Naoki Sawada
2296
	 **/
2297
	protected function fcloseCE($fp, $path='') {
2298
		return (!$this->encoding)? $this->_fclose($fp, $path) : $this->convEncOut($this->_fclose($fp, $this->convEncIn($path)));
2299
	}
2300
	
2301
	/**
2302
	 * Create new file and write into it from file pointer. (with convert encording)
2303
	 * Return new file path or false on error.
2304
	 *
2305
	 * @param  resource  $fp   file pointer
2306
	 * @param  string    $dir  target dir path
2307
	 * @param  string    $name file name
2308
	 * @param  array     $stat file stat (required by some virtual fs)
2309
	 * @return bool|string
2310
	 * @author Naoki Sawada
2311
	 **/
2312
	protected function saveCE($fp, $dir, $name, $stat) {
2313
		return (!$this->encoding)? $this->_save($fp, $dir, $name, $stat) : $this->convEncOut($this->_save($fp, $this->convEncIn($dir), $this->convEncIn($name), $this->convEncIn($stat)));
2314
	}
2315
	
2316
	/**
2317
	 * Return true if path is dir and has at least one childs directory (with convert encording)
2318
	 *
2319
	 * @param  string  $path  dir path
2320
	 * @return bool
2321
	 * @author Naoki Sawada
2322
	 **/
2323
	protected function subdirsCE($path) {
2324
		if (!isset($this->subdirsCache[$path])) {
2325
			$this->subdirsCache[$path] = (!$this->encoding)? $this->_subdirs($path) : $this->convEncOut($this->_subdirs($this->convEncIn($path)));
2326
		}
2327
		return $this->subdirsCache[$path];
2328
	}
2329
	
2330
	/**
2331
	 * Return files list in directory (with convert encording)
2332
	 *
2333
	 * @param  string  $path  dir path
2334
	 * @return array
2335
	 * @author Naoki Sawada
2336
	 **/
2337
	protected function scandirCE($path) {
2338
		return (!$this->encoding)? $this->_scandir($path) : $this->convEncOut($this->_scandir($this->convEncIn($path)));
2339
	}
2340
	
2341
	/**
2342
	 * Create symlink (with convert encording)
2343
	 *
2344
	 * @param  string  $source     file to link to
2345
	 * @param  string  $targetDir  folder to create link in
2346
	 * @param  string  $name       symlink name
2347
	 * @return bool
2348
	 * @author Naoki Sawada
2349
	 **/
2350
	protected function symlinkCE($source, $targetDir, $name) {
2351
		return (!$this->encoding)? $this->_symlink($source, $targetDir, $name) : $this->convEncOut($this->_symlink($this->convEncIn($source), $this->convEncIn($targetDir), $this->convEncIn($name)));
2352
	}
2353
	
2354
	/***************** paths *******************/
2355
	
2356
	/**
2357
	 * Encode path into hash
2358
	 *
2359
	 * @param  string  file path
2360
	 * @return string
2361
	 * @author Dmitry (dio) Levashov
2362
	 * @author Troex Nevelin
2363
	 **/
2364
	protected function encode($path) {
2365
		if ($path !== '') {
2366
2367
			// cut ROOT from $path for security reason, even if hacker decodes the path he will not know the root
2368
			$p = $this->relpathCE($path);
2369
			// if reqesting root dir $path will be empty, then assign '/' as we cannot leave it blank for crypt
2370
			if ($p === '')	{
2371
				$p = DIRECTORY_SEPARATOR;
2372
			}
2373
2374
			// TODO crypt path and return hash
2375
			$hash = $this->crypt($p);
2376
			// hash is used as id in HTML that means it must contain vaild chars
2377
			// make base64 html safe and append prefix in begining
2378
			$hash = strtr(base64_encode($hash), '+/=', '-_.');
2379
			// remove dots '.' at the end, before it was '=' in base64
2380
			$hash = rtrim($hash, '.'); 
2381
			// append volume id to make hash unique
2382
			return $this->id.$hash;
2383
		}
2384
	}
2385
	
2386
	/**
2387
	 * Decode path from hash
2388
	 *
2389
	 * @param  string  file hash
2390
	 * @return string
2391
	 * @author Dmitry (dio) Levashov
2392
	 * @author Troex Nevelin
2393
	 **/
2394
	protected function decode($hash) {
2395
		if (strpos($hash, $this->id) === 0) {
2396
			// cut volume id after it was prepended in encode
2397
			$h = substr($hash, strlen($this->id));
2398
			// replace HTML safe base64 to normal
2399
			$h = base64_decode(strtr($h, '-_.', '+/='));
2400
			// TODO uncrypt hash and return path
2401
			$path = $this->uncrypt($h); 
2402
			// append ROOT to path after it was cut in encode
2403
			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...
2404
		}
2405
	}
2406
	
2407
	/**
2408
	 * Return crypted path 
2409
	 * Not implemented
2410
	 *
2411
	 * @param  string  path
2412
	 * @return mixed
2413
	 * @author Dmitry (dio) Levashov
2414
	 **/
2415
	protected function crypt($path) {
2416
		return $path;
2417
	}
2418
	
2419
	/**
2420
	 * Return uncrypted path 
2421
	 * Not implemented
2422
	 *
2423
	 * @param  mixed  hash
2424
	 * @return mixed
2425
	 * @author Dmitry (dio) Levashov
2426
	 **/
2427
	protected function uncrypt($hash) {
2428
		return $hash;
2429
	}
2430
	
2431
	/**
2432
	 * Validate file name based on $this->options['acceptedName'] regexp or function
2433
	 *
2434
	 * @param  string  $name  file name
2435
	 * @return bool
2436
	 * @author Dmitry (dio) Levashov
2437
	 **/
2438
	protected function nameAccepted($name) {
2439
		if (!json_encode($name)) {
2440
			return false;
2441
		}
2442
		if ($this->nameValidator) {
2443
			if (is_callable($this->nameValidator)) {
2444
				$res = call_user_func($this->nameValidator, $name);
2445
				return $res;
2446
			}
2447
			if (preg_match($this->nameValidator, '') !== false) {
2448
				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...
2449
			}
2450
		}
2451
		return true;
2452
	}
2453
	
2454
	/**
2455
	 * Return new unique name based on file name and suffix
2456
	 *
2457
	 * @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...
2458
	 * @param  string  $suffix  suffix append to name
2459
	 * @return string
2460
	 * @author Dmitry (dio) Levashov
2461
	 **/
2462
	public function uniqueName($dir, $name, $suffix = ' copy', $checkNum = true, $start = 1) {
2463
		$ext  = '';
2464
2465
		if (preg_match('/\.((tar\.(gz|bz|bz2|z|lzo))|cpio\.gz|ps\.gz|xcf\.(gz|bz2)|[a-z0-9]{1,4})$/i', $name, $m)) {
2466
			$ext  = '.'.$m[1];
2467
			$name = substr($name, 0,  strlen($name)-strlen($m[0]));
2468
		} 
2469
		
2470
		if ($checkNum && preg_match('/('.preg_quote($suffix, '/').')(\d*)$/i', $name, $m)) {
2471
			$i    = (int)$m[2];
2472
			$name = substr($name, 0, strlen($name)-strlen($m[2]));
2473
		} else {
2474
			$i     = $start;
2475
			$name .= $suffix;
2476
		}
2477
		$max = $i+100000;
2478
2479
		while ($i <= $max) {
2480
			$n = $name.($i > 0 ? $i : '').$ext;
2481
2482
			if (!$this->stat($this->joinPathCE($dir, $n))) {
2483
				$this->clearcache();
2484
				return $n;
2485
			}
2486
			$i++;
2487
		}
2488
		return $name.md5($dir).$ext;
2489
	}
2490
	
2491
	/**
2492
	 * Converts character encoding from UTF-8 to server's one
2493
	 * 
2494
	 * @param  mixed  $var           target string or array var
2495
	 * @param  bool   $restoreLocale do retore global locale, default is false
2496
	 * @param  string $unknown       replaces character for unknown
2497
	 * @return mixed
2498
	 * @author Naoki Sawada
2499
	 */
2500
	public function convEncIn($var = null, $restoreLocale = false, $unknown = '_') {
2501
		return (!$this->encoding)? $var : $this->convEnc($var, 'UTF-8', $this->encoding, $this->options['locale'], $restoreLocale, $unknown);
2502
	}
2503
	
2504
	/**
2505
	 * Converts character encoding from server's one to UTF-8
2506
	 * 
2507
	 * @param  mixed  $var           target string or array var
2508
	 * @param  bool   $restoreLocale do retore global locale, default is true
2509
	 * @param  string $unknown       replaces character for unknown
2510
	 * @return mixed
2511
	 * @author Naoki Sawada
2512
	 */
2513
	public function convEncOut($var = null, $restoreLocale = true, $unknown = '_') {
2514
		return (!$this->encoding)? $var : $this->convEnc($var, $this->encoding, 'UTF-8', $this->options['locale'], $restoreLocale, $unknown);
2515
	}
2516
	
2517
	/**
2518
	 * Converts character encoding (base function)
2519
	 * 
2520
	 * @param  mixed  $var     target string or array var
2521
	 * @param  string $from    from character encoding
2522
	 * @param  string $to      to character encoding
2523
	 * @param  string $locale  local locale
2524
	 * @param  string $unknown replaces character for unknown
2525
	 * @return mixed
2526
	 */
2527
	protected function convEnc($var, $from, $to, $locale, $restoreLocale, $unknown = '_') {
2528
		if (strtoupper($from) !== strtoupper($to)) {
2529
			if ($locale) {
2530
				@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...
2531
			}
2532
			if (is_array($var)) {
2533
				$_ret = array();
2534
				foreach($var as $_k => $_v) {
2535
					$_ret[$_k] = $this->convEnc($_v, $from, $to, '', false, $unknown = '_');
2536
				}
2537
				$var = $_ret;
2538
			} else {
2539
				$_var = false;
2540
				if (is_string($var)) {
2541
					$_var = $var;
2542
					if (false !== ($_var = @iconv($from, $to.'//TRANSLIT', $_var))) {
2543
						$_var = str_replace('?', $unknown, $_var);
2544
					}
2545
				}
2546
				if  ($_var !== false) {
2547
					$var = $_var;
2548
				}
2549
			}
2550
			if ($restoreLocale) {
2551
				setlocale(LC_ALL, elFinder::$locale);
2552
			}
2553
		}
2554
		return $var;
2555
	}
2556
	
2557
	/*********************** util mainly for inheritance class *********************/
2558
	
2559
	/**
2560
	 * Get temporary filename. Tempfile will be removed when after script execution finishes or exit() is called.
2561
	 * When needing the unique file to a path, give $path to parameter.
2562
	 * 
2563
	 * @param  string       $path for get unique file to a path
2564
	 * @return string|false
2565
	 * @author Naoki Sawada
2566
	 */
2567
	protected function getTempFile($path = '') {
2568
		static $cache = array();
2569
		static $rmfunc;
2570
		
2571
		$key = '';
2572
		if ($path !== '') {
2573
			$key = $this->id . '#' . $path;
2574
			if (isset($cache[$key])) {
2575
				return $cache[$key];
2576
			}
2577
		}
2578
		
2579
		if ($tmpdir = $this->getTempPath()) {
2580
			if (!$rmfunc) {
2581
				$rmfunc = create_function('$f', 'is_file($f) && @unlink($f);');
0 ignored issues
show
Security Best Practice introduced by
The use of create_function is highly discouraged, better use a closure.

create_function can pose a great security vulnerability as it is similar to eval, and could be used for arbitrary code execution. We highly recommend to use a closure instead.

// Instead of
$function = create_function('$a, $b', 'return $a + $b');

// Better use
$function = function($a, $b) { return $a + $b; }
Loading history...
2582
			}
2583
			$name = tempnam($tmpdir, 'ELF');
2584
			if ($key) {
2585
				$cache[$key] = $name;
2586
			}
2587
			register_shutdown_function($rmfunc, $name);
2588
			return $name;
2589
		}
2590
		
2591
		return false;
2592
	}
2593
	
2594
	/**
2595
	 * File path of local server side work file path
2596
	 * 
2597
	 * @param  string $path path need convert encoding to server encoding
2598
	 * @return string
2599
	 * @author Naoki Sawada
2600
	 */
2601
	protected function getWorkFile($path) {
2602
		if ($work = $this->getTempFile()) {
2603
			if ($wfp = fopen($work, 'wb')) {
2604
				if ($fp = $this->_fopen($path)) {
2605
					while(!feof($fp)) {
2606
						fwrite($wfp, fread($fp, 8192));
2607
					}
2608
					$this->_fclose($fp, $path);
2609
					fclose($wfp);
2610
					return $work;
2611
				}
2612
			}
2613
		}
2614
		return false;
2615
	}
2616
	
2617
	/**
2618
	 * Get image size array with `dimensions`
2619
	 *
2620
	 * @param string $path path need convert encoding to server encoding
2621
	 * @param string $mime file mime type
2622
	 * @return array|false
2623
	 */
2624
	public function getImageSize($path, $mime = '') {
2625
		$size = false;
2626
		if ($mime === '' || strtolower(substr($mime, 0, 5)) === 'image') {
2627
			if ($work = $this->getWorkFile($path)) {
2628
				if ($size = @getimagesize($work)) {
2629
					$size['dimensions'] = $size[0].'x'.$size[1];
2630
				}
2631
			}
2632
			is_file($work) && @unlink($work);
2633
		}
2634
		return $size;
2635
	}
2636
	
2637
	/**
2638
	 * Delete dirctory trees
2639
	 *
2640
	 * @param string $localpath path need convert encoding to server encoding
2641
	 * @return boolean
2642
	 * @author Naoki Sawada
2643
	 */
2644
	protected function delTree($localpath) {
2645
		foreach ($this->_scandir($localpath) as $p) {
2646
			@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...
2647
			$stat = $this->stat($this->convEncOut($p));
2648
			$this->convEncIn();
2649
			($stat['mime'] === 'directory')? $this->delTree($p) : $this->_unlink($p);
2650
		}
2651
		return $this->_rmdir($localpath);
2652
	}
2653
	
2654
	/*********************** file stat *********************/
2655
	
2656
	/**
2657
	 * Check file attribute
2658
	 *
2659
	 * @param  string  $path  file path
2660
	 * @param  string  $name  attribute name (read|write|locked|hidden)
2661
	 * @param  bool    $val   attribute value returned by file system
2662
	 * @param  bool    $isDir path is directory (true: directory, false: file)
2663
	 * @return bool
2664
	 * @author Dmitry (dio) Levashov
2665
	 **/
2666
	protected function attr($path, $name, $val=null, $isDir=null) {
2667
		if (!isset($this->defaults[$name])) {
2668
			return false;
2669
		}
2670
		
2671
		
2672
		$perm = null;
2673
		
2674 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...
2675
			$perm = call_user_func($this->access, $name, $path, $this->options['accessControlData'], $this, $isDir);
2676
2677
			if ($perm !== null) {
2678
				return !!$perm;
2679
			}
2680
		}
2681
		
2682
		if ($this->separator != '/') {
2683
			$path = str_replace($this->separator, '/', $this->relpathCE($path));
2684
		} else {
2685
			$path = $this->relpathCE($path);
2686
		}
2687
2688
		$path = '/'.$path;
2689
2690 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...
2691
			$attrs = $this->attributes[$i];
2692
			
2693
			if (isset($attrs[$name]) && isset($attrs['pattern']) && preg_match($attrs['pattern'], $path)) {
2694
				$perm = $attrs[$name];
2695
			} 
2696
		}
2697
		
2698
		return $perm === null ? (is_null($val)? $this->defaults[$name] : $val) : !!$perm;
2699
	}
2700
	
2701
	/**
2702
	 * Return true if file with given name can be created in given folder.
2703
	 *
2704
	 * @param string $dir  parent dir path
2705
	 * @param string $name new file name
2706
	 * @return bool
2707
	 * @author Dmitry (dio) Levashov
2708
	 **/
2709
	protected function allowCreate($dir, $name, $isDir = null) {
2710
		$path = $this->joinPathCE($dir, $name);
2711
		$perm = null;
2712
		
2713 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...
2714
			$perm = call_user_func($this->access, 'write', $path, $this->options['accessControlData'], $this, $isDir);			
2715
			if ($perm !== null) {
2716
				return !!$perm;
2717
			}
2718
		}
2719
		
2720
		$testPath = $this->separator.$this->relpathCE($path);
2721
		
2722 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...
2723
			$attrs = $this->attributes[$i];
2724
			
2725
			if (isset($attrs['write']) && isset($attrs['pattern']) && preg_match($attrs['pattern'], $testPath)) {
2726
				$perm = $attrs['write'];
2727
			} 
2728
		}
2729
		
2730
		return $perm === null ? true : $perm;
2731
	}
2732
	
2733
	/**
2734
	 * Return true if file MIME type can save with check uploadOrder config.
2735
	 * 
2736
	 * @param string $mime
2737
	 * @return boolean
2738
	 */
2739
	protected function allowPutMime($mime) {
2740
		// logic based on http://httpd.apache.org/docs/2.2/mod/mod_authz_host.html#order
2741
		$allow  = $this->mimeAccepted($mime, $this->uploadAllow, null);
2742
		$deny   = $this->mimeAccepted($mime, $this->uploadDeny,  null);
2743
		$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...
2744
		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...
2745
			$res = false; // default is deny
2746
			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...
2747
				$res = true;
2748
			}// else (both match | no match | match only deny) { deny }
2749
		} else { // array('deny', 'allow'), default is to 'allow' - this is the default rule
2750
			$res = true; // default is allow
2751
			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...
2752
				$res = false;
2753
			} // else (both match | no match | match only allow) { allow }
2754
		}
2755
		return $res;
2756
	}
2757
	
2758
	/**
2759
	 * Return fileinfo 
2760
	 *
2761
	 * @param  string  $path  file cache
2762
	 * @return array
2763
	 * @author Dmitry (dio) Levashov
2764
	 **/
2765
	protected function stat($path) {
2766
		if ($path === false || is_null($path)) {
2767
			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...
2768
		}
2769
		$is_root = ($path === $this->root);
2770
		if ($is_root) {
2771
			$rootKey = md5($path);
2772
			if (!isset($this->sessionCache['rootstat'])) {
2773
				$this->sessionCache['rootstat'] = array();
2774
			}
2775
			if (empty($this->ARGS['reload']) || empty($this->ARGS['target']) || strpos($this->ARGS['target'], $this->id) !== 0) {
2776
				// need $path as key for netmount/netunmount
2777
				if (isset($this->sessionCache['rootstat'][$rootKey])) {
2778
					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...
2779
						return $ret;
2780
					}
2781
				}
2782
			}
2783
		}
2784
		$ret = isset($this->cache[$path])
2785
			? $this->cache[$path]
2786
			: $this->updateCache($path, $this->convEncOut($this->_stat($this->convEncIn($path))));
2787
		if ($is_root) {
2788
			$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...
2789
		}
2790
		return $ret;
2791
	}
2792
	
2793
	/**
2794
	 * Put file stat in cache and return it
2795
	 *
2796
	 * @param  string  $path   file path
2797
	 * @param  array   $stat   file stat
2798
	 * @return array
2799
	 * @author Dmitry (dio) Levashov
2800
	 **/
2801
	protected function updateCache($path, $stat) {
2802
		if (empty($stat) || !is_array($stat)) {
2803
			return $this->cache[$path] = array();
2804
		}
2805
2806
		$stat['hash'] = $this->encode($path);
2807
2808
		$root = $path == $this->root;
2809
		$parent = '';
2810
		
2811
		if ($root) {
2812
			if ($this->rootName) {
2813
				$stat['name'] = $this->rootName;
2814
			}
2815
			if (! empty($this->options['icon'])) {
2816
				$stat['icon'] = $this->options['icon'];
2817
			}
2818
			if (! empty($this->options['rootCssClass'])) {
2819
				$stat['csscls'] = $this->options['rootCssClass'];
2820
			}
2821
		} else {
2822
			if (!isset($stat['name']) || $stat['name'] === '') {
2823
				$stat['name'] = $this->basenameCE($path);
2824
			}
2825
			if (empty($stat['phash'])) {
2826
				$parent = $this->dirnameCE($path);
2827
				$stat['phash'] = $this->encode($parent);
2828
			}
2829
		}
2830
		
2831
		// name check
2832
		if (!$jeName = json_encode($stat['name'])) {
2833
			return $this->cache[$path] = array();
2834
		}
2835
		// fix name if required
2836
		if ($this->options['utf8fix'] && $this->options['utf8patterns'] && $this->options['utf8replace']) {
2837
			$stat['name'] = json_decode(str_replace($this->options['utf8patterns'], $this->options['utf8replace'], $jeName));
2838
		}
2839
		
2840
		
2841
		if (empty($stat['mime'])) {
2842
			$stat['mime'] = $this->mimetype($stat['name']);
2843
		}
2844
		
2845
		// @todo move dateformat to client
2846
		// $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...
2847
		// 	? $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...
2848
		// 	: 'unknown';
2849
			
2850
		if (!isset($stat['size'])) {
2851
			$stat['size'] = 'unknown';
2852
		}	
2853
2854
		if ($isDir = ($stat['mime'] === 'directory')) {
2855
			$stat['volumeid'] = $this->id;
2856
		}
2857
		
2858
		$stat['read']  = intval($this->attr($path, 'read', isset($stat['read']) ? !!$stat['read'] : null, $isDir));
2859
		$stat['write'] = intval($this->attr($path, 'write', isset($stat['write']) ? !!$stat['write'] : null, $isDir));
2860
		if ($root) {
2861
			$stat['locked'] = 1;
2862
		} else {
2863
			// lock when parent directory is not writable
2864
			if (!isset($stat['locked'])) {
2865
				$parent = $this->dirnameCE($path);
2866
				$pstat = isset($this->cache[$parent])? $this->cache[$parent] : array();
2867
				if (isset($pstat['write']) && !$pstat['write']) {
2868
					$stat['locked'] = true;
2869
				}
2870
			}
2871
			if ($this->attr($path, 'locked', isset($stat['locked']) ? !!$stat['locked'] : null, $isDir)) {
2872
				$stat['locked'] = 1;
2873
			} else {
2874
				unset($stat['locked']);
2875
			}
2876
		}
2877
2878
		if ($root) {
2879
			unset($stat['hidden']);
2880
		} elseif ($this->attr($path, 'hidden', isset($stat['hidden']) ? !!$stat['hidden'] : null, $isDir) 
2881
		|| !$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...
2882
			$stat['hidden'] = 1;
2883
		} else {
2884
			unset($stat['hidden']);
2885
		}
2886
		
2887
		if ($stat['read'] && empty($stat['hidden'])) {
2888
			
2889
			if ($isDir) {
2890
				// caching parent's subdirs
2891
				if ($parent) {
2892
					$this->subdirsCache[$parent] = true;
2893
				}
2894
				// for dir - check for subdirs
2895
				if ($this->options['checkSubfolders']) {
2896
					if (isset($stat['dirs'])) {
2897
						if ($stat['dirs']) {
2898
							$stat['dirs'] = 1;
2899
						} else {
2900
							unset($stat['dirs']);
2901
						}
2902
					} elseif (!empty($stat['alias']) && !empty($stat['target'])) {
2903
						$stat['dirs'] = isset($this->cache[$stat['target']])
2904
							? intval(isset($this->cache[$stat['target']]['dirs']))
2905
							: $this->subdirsCE($stat['target']);
2906
						
2907
					} elseif ($this->subdirsCE($path)) {
2908
						$stat['dirs'] = 1;
2909
					}
2910
				} else {
2911
					$stat['dirs'] = 1;
2912
				}
2913
			} else {
2914
				// for files - check for thumbnails
2915
				$p = isset($stat['target']) ? $stat['target'] : $path;
2916
				if ($this->tmbURL && !isset($stat['tmb']) && $this->canCreateTmb($p, $stat)) {
2917
					$tmb = $this->gettmb($p, $stat);
2918
					$stat['tmb'] = $tmb ? $tmb : 1;
2919
				}
2920
				
2921
			}
2922
			if (!isset($stat['url']) && $this->URL && $this->encoding) {
2923
				$_path = str_replace($this->separator, '/', substr($path, strlen($this->root) + 1));
2924
				$stat['url'] = rtrim($this->URL, '/') . '/' . str_replace('%2F', '/', rawurlencode((substr(PHP_OS, 0, 3) === 'WIN')? $_path : $this->convEncIn($_path, true)));
2925
			}
2926
		} else {
2927
			if ($isDir) {
2928
				unset($stat['dirs']);
2929
			}
2930
		}
2931
		
2932
		if (!empty($stat['alias']) && !empty($stat['target'])) {
2933
			$stat['thash'] = $this->encode($stat['target']);
2934
			//$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...
2935
			unset($stat['target']);
2936
		}
2937
		
2938
		if (isset($this->options['netkey']) && $path === $this->root) {
2939
			$stat['netkey'] = $this->options['netkey'];
2940
		}
2941
		
2942
		return $this->cache[$path] = $stat;
2943
	}
2944
	
2945
	/**
2946
	 * Get stat for folder content and put in cache
2947
	 *
2948
	 * @param  string  $path
2949
	 * @return void
2950
	 * @author Dmitry (dio) Levashov
2951
	 **/
2952
	protected function cacheDir($path) {
2953
		$this->dirsCache[$path] = array();
2954
		$this->subdirsCache[$path] = false;
2955
2956
		foreach ($this->scandirCE($path) as $p) {
2957 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...
2958
				if ($stat['mime'] === 'directory') {
2959
					$this->subdirsCache[$path] = true;
2960
				}
2961
				$this->dirsCache[$path][] = $p;
2962
			}
2963
		}
2964
	}
2965
	
2966
	/**
2967
	 * Clean cache
2968
	 *
2969
	 * @return void
2970
	 * @author Dmitry (dio) Levashov
2971
	 **/
2972
	protected function clearcache() {
2973
		$this->cache = $this->dirsCache = array();
2974
		unset($this->sessionCache['rootstat'][md5($this->root)]);
2975
	}
2976
	
2977
	/**
2978
	 * Return file mimetype
2979
	 *
2980
	 * @param  string  $path  file path
2981
	 * @return string
2982
	 * @author Dmitry (dio) Levashov
2983
	 **/
2984
	protected function mimetype($path, $name = '') {
2985
		$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...
2986
		
2987
		if ($name === '') {
2988
			$name = $path;
2989
		}
2990
		$ext = (false === $pos = strrpos($name, '.')) ? '' : substr($name, $pos + 1);
2991
		if ($this->mimeDetect == 'finfo') {
2992
			if ($type = @finfo_file($this->finfo, $path)) {
2993
				if ($ext && preg_match('~^application/(?:octet-stream|(?:x-)?zip)~', $type)) {
2994
					if (isset(elFinderVolumeDriver::$mimetypes[$ext])) $type = elFinderVolumeDriver::$mimetypes[$ext];
2995
				} else if ($ext === 'js' && preg_match('~^text/~', $type)) {
2996
					$type = 'text/javascript';
2997
				}
2998
			} else {
2999
				$type = 'unknown';
3000
			}
3001
		} elseif ($this->mimeDetect == 'mime_content_type') {
3002
			$type = mime_content_type($path);
3003
		} else {
3004
			$type = elFinderVolumeDriver::mimetypeInternalDetect($path);
3005
		}
3006
		
3007
		$type = explode(';', $type);
3008
		$type = trim($type[0]);
3009
3010 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...
3011
			// finfo return this mime for empty files
3012
			$type = 'text/plain';
3013
		} elseif ($type == 'application/x-zip') {
3014
			// http://elrte.org/redmine/issues/163
3015
			$type = 'application/zip';
3016
		}
3017
		
3018
		// mime type normalization
3019
		$_checkKey = strtolower($ext.':'.$type);
3020
		if (isset($this->options['mimeMap'][$_checkKey])) {
3021
			$type = $this->options['mimeMap'][$_checkKey];
3022
		}
3023
		
3024
		return $type == 'unknown' && $this->mimeDetect != 'internal'
3025
			? elFinderVolumeDriver::mimetypeInternalDetect($path)
3026
			: $type;
3027
		
3028
	}
3029
	
3030
	/**
3031
	 * Detect file mimetype using "internal" method
3032
	 *
3033
	 * @param  string  $path  file path
3034
	 * @return string
3035
	 * @author Dmitry (dio) Levashov
3036
	 **/
3037
	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...
3038
		$pinfo = pathinfo($path); 
3039
		$ext   = isset($pinfo['extension']) ? strtolower($pinfo['extension']) : '';
3040
		return isset(elFinderVolumeDriver::$mimetypes[$ext]) ? elFinderVolumeDriver::$mimetypes[$ext] : 'unknown';
3041
		
3042
	}
3043
	
3044
	/**
3045
	 * Return file/total directory size
3046
	 *
3047
	 * @param  string  $path  file path
3048
	 * @return int
3049
	 * @author Dmitry (dio) Levashov
3050
	 **/
3051
	protected function countSize($path) {
3052
		$stat = $this->stat($path);
3053
3054
		if (empty($stat) || !$stat['read'] || !empty($stat['hidden'])) {
3055
			return 'unknown';
3056
		}
3057
		
3058
		if ($stat['mime'] != 'directory') {
3059
			return $stat['size'];
3060
		}
3061
		
3062
		$subdirs = $this->options['checkSubfolders'];
3063
		$this->options['checkSubfolders'] = true;
3064
		$result = 0;
3065
		foreach ($this->getScandir($path) as $stat) {
3066
			$size = $stat['mime'] == 'directory' && $stat['read'] 
3067
				? $this->countSize($this->joinPathCE($path, $stat['name'])) 
3068
				: (isset($stat['size']) ? intval($stat['size']) : 0);
3069
			if ($size > 0) {
3070
				$result += $size;
3071
			}
3072
		}
3073
		$this->options['checkSubfolders'] = $subdirs;
3074
		return $result;
3075
	}
3076
	
3077
	/**
3078
	 * Return true if all mimes is directory or files
3079
	 *
3080
	 * @param  string  $mime1  mimetype
3081
	 * @param  string  $mime2  mimetype
3082
	 * @return bool
3083
	 * @author Dmitry (dio) Levashov
3084
	 **/
3085
	protected function isSameType($mime1, $mime2) {
3086
		return ($mime1 == 'directory' && $mime1 == $mime2) || ($mime1 != 'directory' && $mime2 != 'directory');
3087
	}
3088
	
3089
	/**
3090
	 * If file has required attr == $val - return file path,
3091
	 * If dir has child with has required attr == $val - return child path
3092
	 *
3093
	 * @param  string   $path  file path
3094
	 * @param  string   $attr  attribute name
3095
	 * @param  bool     $val   attribute value
3096
	 * @return string|false
3097
	 * @author Dmitry (dio) Levashov
3098
	 **/
3099
	protected function closestByAttr($path, $attr, $val) {
3100
		$stat = $this->stat($path);
3101
		
3102
		if (empty($stat)) {
3103
			return false;
3104
		}
3105
		
3106
		$v = isset($stat[$attr]) ? $stat[$attr] : false;
3107
		
3108
		if ($v == $val) {
3109
			return $path;
3110
		}
3111
3112
		return $stat['mime'] == 'directory'
3113
			? $this->childsByAttr($path, $attr, $val) 
3114
			: false;
3115
	}
3116
	
3117
	/**
3118
	 * Return first found children with required attr == $val
3119
	 *
3120
	 * @param  string   $path  file path
3121
	 * @param  string   $attr  attribute name
3122
	 * @param  bool     $val   attribute value
3123
	 * @return string|false
3124
	 * @author Dmitry (dio) Levashov
3125
	 **/
3126
	protected function childsByAttr($path, $attr, $val) {
3127
		foreach ($this->scandirCE($path) as $p) {
3128
			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...
3129
				return $_p;
3130
			}
3131
		}
3132
		return false;
3133
	}
3134
	
3135
	/*****************  get content *******************/
3136
	
3137
	/**
3138
	 * Return required dir's files info.
3139
	 * If onlyMimes is set - return only dirs and files of required mimes
3140
	 *
3141
	 * @param  string  $path  dir path
3142
	 * @return array
3143
	 * @author Dmitry (dio) Levashov
3144
	 **/
3145
	protected function getScandir($path) {
3146
		$files = array();
3147
		
3148
		!isset($this->dirsCache[$path]) && $this->cacheDir($path);
3149
3150
		foreach ($this->dirsCache[$path] as $p) {
3151
			if (($stat = $this->stat($p)) && empty($stat['hidden'])) {
3152
				$files[] = $stat;
3153
			}
3154
		}
3155
3156
		return $files;
3157
	}
3158
	
3159
	
3160
	/**
3161
	 * Return subdirs tree
3162
	 *
3163
	 * @param  string  $path  parent dir path
3164
	 * @param  int     $deep  tree deep
3165
	 * @return array
3166
	 * @author Dmitry (dio) Levashov
3167
	 **/
3168
	protected function gettree($path, $deep, $exclude='') {
3169
		$dirs = array();
3170
		
3171
		!isset($this->dirsCache[$path]) && $this->cacheDir($path);
3172
3173
		foreach ($this->dirsCache[$path] as $p) {
3174
			$stat = $this->stat($p);
3175
			
3176
			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...
3177
				$dirs[] = $stat;
3178
				if ($deep > 0 && !empty($stat['dirs'])) {
3179
					$dirs = array_merge($dirs, $this->gettree($p, $deep-1));
3180
				}
3181
			}
3182
		}
3183
3184
		return $dirs;
3185
	}	
3186
		
3187
	/**
3188
	 * Recursive files search
3189
	 *
3190
	 * @param  string  $path   dir path
3191
	 * @param  string  $q      search string
3192
	 * @param  array   $mimes
3193
	 * @return array
3194
	 * @author Dmitry (dio) Levashov
3195
	 **/
3196
	protected function doSearch($path, $q, $mimes) {
3197
		$result = array();
3198
3199
		foreach($this->scandirCE($path) as $p) {
3200
			@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...
3201
			$stat = $this->stat($p);
3202
3203
			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...
3204
				continue;
3205
			}
3206
3207
			if (!empty($stat['hidden']) || !$this->mimeAccepted($stat['mime'], $mimes)) {
3208
				continue;
3209
			}
3210
			
3211
			$name = $stat['name'];
3212
3213
			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...
3214
				$stat['path'] = $this->path($stat['hash']);
3215
				if ($this->URL && !isset($stat['url'])) {
3216
					$path = str_replace($this->separator, '/', substr($p, strlen($this->root) + 1));
3217
					if ($this->encoding) {
3218
						$path = str_replace('%2F', '/', rawurlencode($this->convEncIn($path, true)));
3219
					}
3220
					$stat['url'] = $this->URL . $path;
3221
				}
3222
				
3223
				$result[] = $stat;
3224
			}
3225 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...
3226
				$result = array_merge($result, $this->doSearch($p, $q, $mimes));
3227
			}
3228
		}
3229
		
3230
		return $result;
3231
	}
3232
		
3233
	/**********************  manuipulations  ******************/
3234
		
3235
	/**
3236
	 * Copy file/recursive copy dir only in current volume.
3237
	 * Return new file path or false.
3238
	 *
3239
	 * @param  string  $src   source path
3240
	 * @param  string  $dst   destination dir path
3241
	 * @param  string  $name  new file name (optionaly)
3242
	 * @return string|false
3243
	 * @author Dmitry (dio) Levashov
3244
	 **/
3245
	protected function copy($src, $dst, $name) {
3246
		$srcStat = $this->stat($src);
3247
		$this->clearcache();
3248
		
3249
		if (!empty($srcStat['thash'])) {
3250
			$target = $this->decode($srcStat['thash']);
3251
			if (!$this->inpathCE($target, $this->root)) {
3252
				return $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']), elFinder::ERROR_MKOUTLINK);
3253
			}
3254
			$stat   = $this->stat($target);
3255
			$this->clearcache();
3256
			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...
3257
				? $this->joinPathCE($dst, $name)
3258
				: $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']));
3259
		} 
3260
		
3261
		if ($srcStat['mime'] == 'directory') {
3262
			$test = $this->stat($this->joinPathCE($dst, $name));
3263
			
3264 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...
3265
				return $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']));
3266
			}
3267
			
3268
			$dst = $this->joinPathCE($dst, $name);
3269
			
3270
			foreach ($this->getScandir($src) as $stat) {
3271
				if (empty($stat['hidden'])) {
3272
					$name = $stat['name'];
3273
					if (!$this->copy($this->joinPathCE($src, $name), $dst, $name)) {
3274
						$this->remove($dst, true); // fall back
3275
						return $this->setError($this->error, elFinder::ERROR_COPY, $this->_path($src));
3276
					}
3277
				}
3278
			}
3279
			$this->clearcache();
3280
			return $dst;
3281
		} 
3282
3283
		return $this->convEncOut($this->_copy($this->convEncIn($src), $this->convEncIn($dst), $this->convEncIn($name)))
3284
			? $this->joinPathCE($dst, $name) 
3285
			: $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']));
3286
	}
3287
3288
	/**
3289
	 * Move file
3290
	 * Return new file path or false.
3291
	 *
3292
	 * @param  string  $src   source path
3293
	 * @param  string  $dst   destination dir path
3294
	 * @param  string  $name  new file name 
3295
	 * @return string|false
3296
	 * @author Dmitry (dio) Levashov
3297
	 **/
3298
	protected function move($src, $dst, $name) {
3299
		$stat = $this->stat($src);
3300
		$stat['realpath'] = $src;
3301
		$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...
3302
		$this->clearcache();
3303
		
3304 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...
3305
			$this->removed[] = $stat;
3306
3307
			return $this->joinPathCE($dst, $name);
3308
		}
3309
3310
		return $this->setError(elFinder::ERROR_MOVE, $this->path($stat['hash']));
3311
	}
3312
3313
	/**
3314
	 * Copy file from another volume.
3315
	 * Return new file path or false.
3316
	 *
3317
	 * @param  Object  $volume       source volume
3318
	 * @param  string  $src          source file hash
3319
	 * @param  string  $destination  destination dir path
3320
	 * @param  string  $name         file name
3321
	 * @return string|false
3322
	 * @author Dmitry (dio) Levashov
3323
	 **/
3324
	protected function copyFrom($volume, $src, $destination, $name) {
3325
		
3326
		if (($source = $volume->file($src)) == false) {
3327
			return $this->setError(elFinder::ERROR_COPY, '#'.$src, $volume->error());
3328
		}
3329
		
3330
		$errpath = $volume->path($source['hash']);
3331
		
3332
		if (!$this->nameAccepted($source['name'])) {
3333
			return $this->setError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_INVALID_NAME);
3334
		}
3335
				
3336
		if (!$source['read']) {
3337
			return $this->setError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_PERM_DENIED);
3338
		}
3339
		
3340
		if ($source['mime'] == 'directory') {
3341
			$stat = $this->stat($this->joinPathCE($destination, $name));
3342
			$this->clearcache();
3343 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...
3344
				return $this->setError(elFinder::ERROR_COPY, $errpath);
3345
			}
3346
			
3347
			$path = $this->joinPathCE($destination, $name);
3348
			
3349
			foreach ($volume->scandir($src) as $entr) {
3350
				if (!$this->copyFrom($volume, $entr['hash'], $path, $entr['name'])) {
3351
					$this->remove($path, true); // fall back
3352
					return $this->setError($this->error, elFinder::ERROR_COPY, $errpath);
3353
				}
3354
			}
3355
			
3356
		} else {
3357
			// $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...
3358
			// $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...
3359
			if (($dim = $volume->dimensions($src))) {
3360
				$s = explode('x', $dim);
3361
				$source['width']  = $s[0];
3362
				$source['height'] = $s[1];
3363
			}
3364
			
3365
			if (($fp = $volume->open($src)) == false
3366
			|| ($path = $this->saveCE($fp, $destination, $name, $source)) == false) {
3367
				$fp && $volume->close($fp, $src);
3368
				return $this->setError(elFinder::ERROR_COPY, $errpath);
3369
			}
3370
			$volume->close($fp, $src);
3371
			
3372
			// MIME check
3373
			$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 3366 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...
3374
			$mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($stat['name']);
3375
			if ($stat['mime'] === $mimeByName) {
3376
				$mimeByName = '';
3377
			}
3378 View Code Duplication
			if (!$this->allowPutMime($stat['mime']) || ($mimeByName !== 'unknown' && !$this->allowPutMime($mimeByName))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3379
				$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 3366 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...
3380
				return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME, $errpath);
3381
			}
3382
		}
3383
		
3384
		return $path;
3385
	}
3386
		
3387
	/**
3388
	 * Remove file/ recursive remove dir
3389
	 *
3390
	 * @param  string  $path   file path
3391
	 * @param  bool    $force  try to remove even if file locked
3392
	 * @return bool
3393
	 * @author Dmitry (dio) Levashov
3394
	 **/
3395
	protected function remove($path, $force = false) {
3396
		$stat = $this->stat($path);
3397
		
3398
		if (empty($stat)) {
3399
			return $this->setError(elFinder::ERROR_RM, $this->path($stat['hash']), elFinder::ERROR_FILE_NOT_FOUND);
3400
		}
3401
		
3402
		$stat['realpath'] = $path;
3403
		$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...
3404
		$this->clearcache();
3405
		
3406 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...
3407
			return $this->setError(elFinder::ERROR_LOCKED, $this->path($stat['hash']));
3408
		}
3409
		
3410
		if ($stat['mime'] == 'directory' && empty($stat['thash'])) {
3411
			$ret = $this->delTree($this->convEncIn($path));
3412
			$this->convEncOut();
3413
			if (!$ret) {
3414
				return $this->setError(elFinder::ERROR_RM, $this->path($stat['hash']));
3415
			}
3416 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...
3417
			if ($this->convEncOut(!$this->_unlink($this->convEncIn($path)))) {
3418
				return $this->setError(elFinder::ERROR_RM, $this->path($stat['hash']));
3419
			}
3420
		}
3421
3422
		$this->removed[] = $stat;
3423
		return true;
3424
	}
3425
	
3426
3427
	/************************* thumbnails **************************/
3428
		
3429
	/**
3430
	 * Return thumbnail file name for required file
3431
	 *
3432
	 * @param  array  $stat  file stat
3433
	 * @return string
3434
	 * @author Dmitry (dio) Levashov
3435
	 **/
3436
	protected function tmbname($stat) {
3437
		return $stat['hash'].$stat['ts'].'.png';
3438
	}
3439
	
3440
	/**
3441
	 * Return thumnbnail name if exists
3442
	 *
3443
	 * @param  string  $path file path
3444
	 * @param  array   $stat file stat
3445
	 * @return string|false
3446
	 * @author Dmitry (dio) Levashov
3447
	 **/
3448
	protected function gettmb($path, $stat) {
3449
		if ($this->tmbURL && $this->tmbPath) {
3450
			// file itself thumnbnail
3451
			if (strpos($path, $this->tmbPath) === 0) {
3452
				return basename($path);
3453
			}
3454
3455
			$name = $this->tmbname($stat);
3456
			if (file_exists($this->tmbPath.DIRECTORY_SEPARATOR.$name)) {
3457
				return $name;
3458
			}
3459
		}
3460
		return false;
3461
	}
3462
	
3463
	/**
3464
	 * Return true if thumnbnail for required file can be created
3465
	 *
3466
	 * @param  string  $path  thumnbnail path 
3467
	 * @param  array   $stat  file stat
3468
	 * @param  bool    $checkTmbPath
3469
	 * @return string|bool
3470
	 * @author Dmitry (dio) Levashov
3471
	 **/
3472
	protected function canCreateTmb($path, $stat, $checkTmbPath = true) {
3473
		return (!$checkTmbPath || $this->tmbPathWritable) 
3474
			&& (!$this->tmbPath || strpos($path, $this->tmbPath) === false) // do not create thumnbnail for thumnbnail
3475
			&& $this->imgLib 
3476
			&& strpos($stat['mime'], 'image') === 0 
3477
			&& ($this->imgLib == 'gd' ? $stat['mime'] == 'image/jpeg' || $stat['mime'] == 'image/png' || $stat['mime'] == 'image/gif' : true);
3478
	}
3479
	
3480
	/**
3481
	 * Return true if required file can be resized.
3482
	 * By default - the same as canCreateTmb
3483
	 *
3484
	 * @param  string  $path  thumnbnail path 
3485
	 * @param  array   $stat  file stat
3486
	 * @return string|bool
3487
	 * @author Dmitry (dio) Levashov
3488
	 **/
3489
	protected function canResize($path, $stat) {
3490
		return $this->canCreateTmb($path, $stat, false);
3491
	}
3492
	
3493
	/**
3494
	 * Create thumnbnail and return it's URL on success
3495
	 *
3496
	 * @param  string  $path  file path
3497
	 * @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...
3498
	 * @return string|false
3499
	 * @author Dmitry (dio) Levashov
3500
	 **/
3501
	protected function createTmb($path, $stat) {
3502
		if (!$stat || !$this->canCreateTmb($path, $stat)) {
3503
			return false;
3504
		}
3505
3506
		$name = $this->tmbname($stat);
3507
		$tmb  = $this->tmbPath.DIRECTORY_SEPARATOR.$name;
3508
3509
		// copy image into tmbPath so some drivers does not store files on local fs
3510
		if (($src = $this->fopenCE($path, 'rb')) == false) {
3511
			return false;
3512
		}
3513
3514
		if (($trg = fopen($tmb, 'wb')) == false) {
3515
			$this->fcloseCE($src, $path);
3516
			return false;
3517
		}
3518
3519
		while (!feof($src)) {
3520
			fwrite($trg, fread($src, 8192));
3521
		}
3522
3523
		$this->fcloseCE($src, $path);
3524
		fclose($trg);
3525
3526
		$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...
3527
		
3528
		$tmbSize = $this->tmbSize;
3529
		
3530
		if (($s = getimagesize($tmb)) == false) {
3531
			return false;
3532
		}
3533
3534
		/* If image smaller or equal thumbnail size - just fitting to thumbnail square */
3535
		if ($s[0] <= $tmbSize && $s[1]	<= $tmbSize) {
3536
			$result = $this->imgSquareFit($tmb, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png' );
3537
		} else {
3538
		
3539
			if ($this->options['tmbCrop']) {
3540
		
3541
				$result = $tmb;
3542
				/* Resize and crop if image bigger than thumbnail */
3543 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...
3544
					$result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, false, 'png');
3545
				}
3546
		
3547 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...
3548
					$x = $s[0] > $tmbSize ? intval(($s[0] - $tmbSize)/2) : 0;
3549
					$y = $s[1] > $tmbSize ? intval(($s[1] - $tmbSize)/2) : 0;
3550
					$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...
3551
				} else {
3552
					$result = false;
3553
				}
3554
		
3555
			} else {
3556
				$result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, true, 'png');
3557
			}
3558
		
3559
			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...
3560
				$result = $this->imgSquareFit($result, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png' );
3561
			}
3562
		}
3563
		
3564
		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...
3565
			unlink($tmb);
3566
			return false;
3567
		}
3568
		
3569
		return $name;
3570
	}
3571
3572
	/**
3573
	 * Resize image
3574
	 *
3575
	 * @param  string   $path               image file
3576
	 * @param  int      $width              new width
3577
	 * @param  int      $height             new height
3578
	 * @param  bool	    $keepProportions    crop image
3579
	 * @param  bool	    $resizeByBiggerSide resize image based on bigger side if true
3580
	 * @param  string   $destformat         image destination format
3581
	 * @return string|false
3582
	 * @author Dmitry (dio) Levashov
3583
	 * @author Alexey Sukhotin
3584
	 **/
3585
	protected function imgResize($path, $width, $height, $keepProportions = false, $resizeByBiggerSide = true, $destformat = null) {
3586
		if (($s = @getimagesize($path)) == false) {
3587
			return false;
3588
		}
3589
3590
		$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...
3591
		
3592
		list($size_w, $size_h) = array($width, $height);
3593
	
3594
		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...
3595
		
3596
			list($orig_w, $orig_h) = array($s[0], $s[1]);
3597
		
3598
			/* Resizing by biggest side */
3599
			if ($resizeByBiggerSide) {
3600 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...
3601
					$size_h = round($orig_h * $width / $orig_w);
3602
					$size_w = $width;
3603
				} else {
3604
					$size_w = round($orig_w * $height / $orig_h);
3605
					$size_h = $height;
3606
				}
3607 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...
3608
				if ($orig_w > $orig_h) {
3609
					$size_w = round($orig_w * $height / $orig_h);
3610
					$size_h = $height;
3611
				} else {
3612
					$size_h = round($orig_h * $width / $orig_w);
3613
					$size_w = $width;
3614
				}
3615
			}
3616
		}
3617
3618
		switch ($this->imgLib) {
3619
			case 'imagick':
3620
				
3621
				try {
3622
					$img = new imagick($path);
3623
				} catch (Exception $e) {
3624
					return false;
3625
				}
3626
3627
				// Imagick::FILTER_BOX faster than FILTER_LANCZOS so use for createTmb
3628
				// 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...
3629
				// 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...
3630
				$filter = ($destformat === 'png' /* createTmb */)? Imagick::FILTER_BOX : Imagick::FILTER_LANCZOS;
3631
				
3632
				$ani = ($img->getNumberImages() > 1);
3633
				if ($ani && is_null($destformat)) {
3634
					$img = $img->coalesceImages();
3635
					do {
3636
						$img->resizeImage($size_w, $size_h, $filter, 1);
3637
					} while ($img->nextImage());
3638
					$img = $img->optimizeImageLayers();
3639
					$result = $img->writeImages($path, true);
0 ignored issues
show
Bug introduced by
The method writeImages cannot be called on $img (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
3640
				} else {
3641
					if ($ani) {
3642
						$img->setFirstIterator();
3643
					}
3644
					$img->resizeImage($size_w, $size_h, $filter, 1);
3645
					$result = $img->writeImage($path);
3646
				}
3647
				
3648
				$img->destroy();
3649
3650
				return $result ? $path : false;
3651
3652
				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...
3653
3654
			case 'gd':
3655
				$img = self::gdImageCreate($path,$s['mime']);
3656
3657
				if ($img &&  false != ($tmp = imagecreatetruecolor($size_w, $size_h))) {
3658
				
3659
					self::gdImageBackground($tmp,$this->options['tmbBgColor']);
3660
					
3661
					if (!imagecopyresampled($tmp, $img, 0, 0, 0, 0, $size_w, $size_h, $s[0], $s[1])) {
3662
						return false;
3663
					}
3664
		
3665
					$result = self::gdImage($tmp, $path, $destformat, $s['mime']);
3666
3667
					imagedestroy($img);
3668
					imagedestroy($tmp);
3669
3670
					return $result ? $path : false;
3671
3672
				}
3673
				break;
3674
		}
3675
		
3676
		return false;
3677
  	}
3678
  
3679
	/**
3680
	 * Crop image
3681
	 *
3682
	 * @param  string   $path               image file
3683
	 * @param  int      $width              crop width
3684
	 * @param  int      $height             crop height
3685
	 * @param  bool	    $x                  crop left offset
3686
	 * @param  bool	    $y                  crop top offset
3687
	 * @param  string   $destformat         image destination format
3688
	 * @return string|false
3689
	 * @author Dmitry (dio) Levashov
3690
	 * @author Alexey Sukhotin
3691
	 **/
3692
  	protected function imgCrop($path, $width, $height, $x, $y, $destformat = null) {
3693
		if (($s = @getimagesize($path)) == false) {
3694
			return false;
3695
		}
3696
3697
		$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...
3698
		
3699
		switch ($this->imgLib) {
3700
			case 'imagick':
3701
				
3702
				try {
3703
					$img = new imagick($path);
3704
				} catch (Exception $e) {
3705
					return false;
3706
				}
3707
				
3708
				$ani = ($img->getNumberImages() > 1);
3709
				if ($ani && is_null($destformat)) {
3710
					$img = $img->coalesceImages();
3711
					do {
3712
						$img->setImagePage($s[0], $s[1], 0, 0);
3713
						$img->cropImage($width, $height, $x, $y);
3714
						$img->setImagePage($width, $height, 0, 0);
3715
					} while ($img->nextImage());
3716
					$img = $img->optimizeImageLayers();
3717
					$result = $img->writeImages($path, true);
0 ignored issues
show
Bug introduced by
The method writeImages cannot be called on $img (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
3718
				} else {
3719
					if ($ani) {
3720
						$img->setFirstIterator();
3721
					}
3722
					$img->setImagePage($s[0], $s[1], 0, 0);
3723
					$img->cropImage($width, $height, $x, $y);
3724
					$img->setImagePage($width, $height, 0, 0);
3725
					$result = $img->writeImage($path);
3726
				}
3727
				
3728
				$img->destroy();
3729
3730
				return $result ? $path : false;
3731
3732
				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...
3733
3734
			case 'gd':
3735
				$img = self::gdImageCreate($path,$s['mime']);
3736
3737
				if ($img &&  false != ($tmp = imagecreatetruecolor($width, $height))) {
3738
					
3739
					self::gdImageBackground($tmp,$this->options['tmbBgColor']);
3740
3741
					$size_w = $width;
3742
					$size_h = $height;
3743
3744
					if ($s[0] < $width || $s[1] < $height) {
3745
						$size_w = $s[0];
3746
						$size_h = $s[1];
3747
					}
3748
3749
					if (!imagecopy($tmp, $img, 0, 0, $x, $y, $size_w, $size_h)) {
3750
						return false;
3751
					}
3752
					
3753
					$result = self::gdImage($tmp, $path, $destformat, $s['mime']);
3754
3755
					imagedestroy($img);
3756
					imagedestroy($tmp);
3757
3758
					return $result ? $path : false;
3759
3760
				}
3761
				break;
3762
		}
3763
3764
		return false;
3765
	}
3766
3767
	/**
3768
	 * Put image to square
3769
	 *
3770
	 * @param  string   $path               image file
3771
	 * @param  int      $width              square width
3772
	 * @param  int      $height             square height
3773
	 * @param  int	    $align              reserved
3774
	 * @param  int 	    $valign             reserved
3775
	 * @param  string   $bgcolor            square background color in #rrggbb format
3776
	 * @param  string   $destformat         image destination format
3777
	 * @return string|false
3778
	 * @author Dmitry (dio) Levashov
3779
	 * @author Alexey Sukhotin
3780
	 **/
3781
	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...
3782
		if (($s = @getimagesize($path)) == false) {
3783
			return false;
3784
		}
3785
3786
		$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...
3787
3788
		/* Coordinates for image over square aligning */
3789
		$y = ceil(abs($height - $s[1]) / 2); 
3790
		$x = ceil(abs($width - $s[0]) / 2);
3791
3792
		switch ($this->imgLib) {
3793
			case 'imagick':
3794
				try {
3795
					$img = new imagick($path);
3796
				} catch (Exception $e) {
3797
					return false;
3798
				}
3799
				
3800
				$ani = ($img->getNumberImages() > 1);
3801
				if ($ani && is_null($destformat)) {
3802
					$img1 = new Imagick();
3803
					$img1->setFormat('gif');
3804
					$img = $img->coalesceImages();
3805
					do {
3806
						$gif = new Imagick();
3807
						$gif->newImage($width, $height, new ImagickPixel($bgcolor));
3808
						$gif->setImageColorspace($img->getImageColorspace());
3809
						$gif->setImageFormat('gif');
3810
						$gif->compositeImage( $img, imagick::COMPOSITE_OVER, $x, $y );
3811
						$gif->setImageDelay($img->getImageDelay());
3812
						$gif->setImageIterations($img->getImageIterations());
3813
						$img1->addImage($gif);
3814
						$gif->destroy();
3815
					} while ($img->nextImage());
3816
					$img1 = $img1->optimizeImageLayers();
3817
					$result = $img1->writeImages($path, true);
0 ignored issues
show
Bug introduced by
The method writeImages cannot be called on $img1 (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
3818
				} else {
3819
					if ($ani) {
3820
						$img->setFirstIterator();
3821
					}
3822
					$img1 = new Imagick();
3823
					$img1->newImage($width, $height, new ImagickPixel($bgcolor));
3824
					$img1->setImageColorspace($img->getImageColorspace());
3825
					$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...
3826
					$img1->compositeImage( $img, imagick::COMPOSITE_OVER, $x, $y );
3827
					$result = $img1->writeImage($path);
3828
				}
3829
				
3830
				$img1->destroy();
3831
				$img->destroy();
3832
				return $result ? $path : false;
3833
3834
				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...
3835
3836
			case 'gd':
3837
				$img = self::gdImageCreate($path,$s['mime']);
3838
3839
				if ($img &&  false != ($tmp = imagecreatetruecolor($width, $height))) {
3840
3841
					self::gdImageBackground($tmp,$bgcolor);
3842
3843
					if (!imagecopy($tmp, $img, $x, $y, 0, 0, $s[0], $s[1])) {
3844
						return false;
3845
					}
3846
3847
					$result = self::gdImage($tmp, $path, $destformat, $s['mime']);
3848
3849
					imagedestroy($img);
3850
					imagedestroy($tmp);
3851
3852
					return $result ? $path : false;
3853
				}
3854
				break;
3855
		}
3856
3857
		return false;
3858
	}
3859
3860
	/**
3861
	 * Rotate image
3862
	 *
3863
	 * @param  string   $path               image file
3864
	 * @param  int      $degree             rotete degrees
3865
	 * @param  string   $bgcolor            square background color in #rrggbb format
3866
	 * @param  string   $destformat         image destination format
3867
	 * @return string|false
3868
	 * @author nao-pon
3869
	 * @author Troex Nevelin
3870
	 **/
3871
	protected function imgRotate($path, $degree, $bgcolor = '#ffffff', $destformat = null) {
3872
		if (($s = @getimagesize($path)) == false || $degree % 360 === 0) {
3873
			return false;
3874
		}
3875
3876
		$result = false;
3877
3878
		// try lossless rotate
3879
		if ($degree % 90 === 0 && in_array($s[2], array(IMAGETYPE_JPEG, IMAGETYPE_JPEG2000))) {
3880
			$count = ($degree / 90) % 4;
3881
			$exiftran = array(
3882
				1 => '-9',
3883
				2 => '-1',
3884
				3 => '-2'
3885
			);
3886
			$jpegtran = array(
3887
				1 => '90',
3888
				2 => '180',
3889
				3 => '270'
3890
			);
3891
			$quotedPath = escapeshellarg($path);
3892
			$cmds = array(
3893
				'exiftran -i '.$exiftran[$count].' '.$path,
3894
				'jpegtran -rotate '.$jpegtran[$count].' -copy all -outfile '.$quotedPath.' '.$quotedPath
3895
			);
3896
			foreach($cmds as $cmd) {
3897
				if ($this->procExec($cmd) === 0) {
3898
					$result = true;
3899
					break;
3900
				}
3901
			}
3902
			if ($result) {
3903
				return $path;
3904
			}
3905
		}
3906
3907
		switch ($this->imgLib) {
3908
			case 'imagick':
3909
				try {
3910
					$img = new imagick($path);
3911
				} catch (Exception $e) {
3912
					return false;
3913
				}
3914
3915
				if ($img->getNumberImages() > 1) {
3916
					$img = $img->coalesceImages();
3917
					do {
3918
						$img->rotateImage(new ImagickPixel($bgcolor), $degree);
3919
					} while ($img->nextImage());
3920
					$img = $img->optimizeImageLayers();
3921
					$result = $img->writeImages($path, true);
0 ignored issues
show
Bug introduced by
The method writeImages cannot be called on $img (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
3922
				} else {
3923
					$img->rotateImage(new ImagickPixel($bgcolor), $degree);
3924
					$result = $img->writeImage($path);
3925
				}
3926
				$img->destroy();
3927
				return $result ? $path : false;
3928
3929
				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...
3930
3931
			case 'gd':
3932
				$img = self::gdImageCreate($path,$s['mime']);
3933
3934
				$degree = 360 - $degree;
3935
				list($r, $g, $b) = sscanf($bgcolor, "#%02x%02x%02x");
3936
				$bgcolor = imagecolorallocate($img, $r, $g, $b);
3937
				$tmp = imageRotate($img, $degree, (int)$bgcolor);
3938
3939
				$result = self::gdImage($tmp, $path, $destformat, $s['mime']);
3940
3941
				imageDestroy($img);
3942
				imageDestroy($tmp);
3943
3944
				return $result ? $path : false;
3945
3946
				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...
3947
		}
3948
3949
		return false;
3950
	}
3951
3952
	/**
3953
	 * Execute shell command
3954
	 *
3955
	 * @param  string  $command       command line
3956
	 * @param  array   $output        stdout strings
3957
	 * @param  array   $return_var    process exit code
3958
	 * @param  array   $error_output  stderr strings
3959
	 * @return int     exit code
3960
	 * @author Alexey Sukhotin
3961
	 **/
3962
	protected function procExec($command , array &$output = null, &$return_var = -1, array &$error_output = null) {
3963
3964
		$descriptorspec = array(
3965
			0 => array("pipe", "r"),  // stdin
3966
			1 => array("pipe", "w"),  // stdout
3967
			2 => array("pipe", "w")   // stderr
3968
		);
3969
3970
		$process = proc_open($command, $descriptorspec, $pipes, null, null);
3971
3972
		if (is_resource($process)) {
3973
3974
			fclose($pipes[0]);
3975
3976
			$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...
3977
			$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...
3978
3979
			$output = stream_get_contents($pipes[1]);
3980
			$error_output = stream_get_contents($pipes[2]);
3981
3982
			fclose($pipes[1]);
3983
			fclose($pipes[2]);
3984
			$return_var = proc_close($process);
3985
3986
3987
		}
3988
		
3989
		return $return_var;
3990
		
3991
	}
3992
3993
	/**
3994
	 * Remove thumbnail, also remove recursively if stat is directory
3995
	 *
3996
	 * @param  string  $stat  file stat
3997
	 * @return void
3998
	 * @author Dmitry (dio) Levashov
3999
	 * @author Naoki Sawada
4000
	 * @author Troex Nevelin
4001
	 **/
4002
	protected function rmTmb($stat) {
4003
		if ($stat['mime'] === 'directory') {
4004
			foreach ($this->scandirCE($this->decode($stat['hash'])) as $p) {
4005
				@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...
4006
				$name = $this->basenameCE($p);
4007
				$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...
4008
			}
4009
		} else if (!empty($stat['tmb']) && $stat['tmb'] != "1") {
4010
			$tmb = $this->tmbPath.DIRECTORY_SEPARATOR.$stat['tmb'];
4011
			file_exists($tmb) && @unlink($tmb);
4012
			clearstatcache();
4013
		}
4014
	}
4015
	
4016
	/**
4017
	 * Create an gd image according to the specified mime type
4018
	 *
4019
	 * @param string $path image file
4020
	 * @param string $mime
4021
	 * @return gd image resource identifier
4022
	 */
4023
	protected function gdImageCreate($path,$mime){
4024
		switch($mime){
4025
			case 'image/jpeg':
4026
			return imagecreatefromjpeg($path);
4027
4028
			case 'image/png':
4029
			return imagecreatefrompng($path);
4030
4031
			case 'image/gif':
4032
			return imagecreatefromgif($path);
4033
4034
			case 'image/xbm':
4035
			return imagecreatefromxbm($path);
4036
		}
4037
		return false;
4038
	}
4039
4040
	/**
4041
	 * Output gd image to file
4042
	 *
4043
	 * @param resource $image gd image resource
4044
	 * @param string $filename The path to save the file to.
4045
	 * @param string $destformat The Image type to use for $filename
4046
	 * @param string $mime The original image mime type
4047
	 */
4048
	protected function gdImage($image, $filename, $destformat, $mime ){
4049
4050
		if ($destformat == 'jpg' || ($destformat == null && $mime == 'image/jpeg')) {
4051
			return imagejpeg($image, $filename, 100);
4052
		}
4053
4054
		if ($destformat == 'gif' || ($destformat == null && $mime == 'image/gif')) {
4055
			return imagegif($image, $filename, 7);
4056
		}
4057
4058
		return imagepng($image, $filename, 7);
4059
	}
4060
4061
	/**
4062
	 * Assign the proper background to a gd image
4063
	 *
4064
	 * @param resource $image gd image resource
4065
	 * @param string $bgcolor background color in #rrggbb format
4066
	 */
4067
	protected function gdImageBackground($image, $bgcolor){
4068
4069
		if( $bgcolor == 'transparent' ){
4070
			imagesavealpha($image,true);
4071
			$bgcolor1 = imagecolorallocatealpha($image, 255, 255, 255, 127);
4072
4073
		}else{
4074
			list($r, $g, $b) = sscanf($bgcolor, "#%02x%02x%02x");
4075
			$bgcolor1 = imagecolorallocate($image, $r, $g, $b);
4076
		}
4077
4078
		imagefill($image, 0, 0, $bgcolor1);
4079
	}
4080
4081
	/*********************** misc *************************/
4082
	
4083
	/**
4084
	 * Return smart formatted date
4085
	 *
4086
	 * @param  int     $ts  file timestamp
4087
	 * @return string
4088
	 * @author Dmitry (dio) Levashov
4089
	 **/
4090
	// 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...
4091
	// 	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...
4092
	// 		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...
4093
	// 	}
4094
	// 	
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...
4095
	// 	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...
4096
	// 		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...
4097
	// 	} 
4098
	// 	
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...
4099
	// 	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...
4100
	// }
4101
4102
	/**
4103
	* Find position of first occurrence of string in a string with multibyte support
4104
	*
4105
	* @param  string  $haystack  The string being checked.
4106
	* @param  string  $needle    The string to find in haystack.
4107
	* @param  int     $offset    The search offset. If it is not specified, 0 is used.
4108
	* @return int|bool
4109
	* @author Alexey Sukhotin
4110
	**/
4111
	protected function stripos($haystack , $needle , $offset = 0) {
4112
		if (function_exists('mb_stripos')) {
4113
			return mb_stripos($haystack , $needle , $offset, 'UTF-8');
4114
		} else if (function_exists('mb_strtolower') && function_exists('mb_strpos')) {
4115
			return mb_strpos(mb_strtolower($haystack, 'UTF-8'), mb_strtolower($needle, 'UTF-8'), $offset);
4116
		} 
4117
		return stripos($haystack , $needle , $offset);
4118
	}
4119
4120
	/**
4121
	 * Get server side available archivers
4122
	 * 
4123
	 * @param bool $use_cache
4124
	 * @return array
4125
	 */
4126
	protected function getArchivers($use_cache = true) {
4127
4128
		$sessionKey = 'ARCHIVERS_CACHE';
4129
		if ($use_cache && isset($this->sessionCache[$sessionKey]) && is_array($this->sessionCache[$sessionKey])) {
4130
			return $this->sessionCache[$sessionKey];
4131
		}
4132
		
4133
		$arcs = array(
4134
			'create'  => array(),
4135
			'extract' => array()
4136
		);
4137
		
4138
		if (function_exists('proc_open')) {
4139
		
4140
			$this->procExec('tar --version', $o, $ctar);
4141
			
4142
			if ($ctar == 0) {
4143
				$arcs['create']['application/x-tar']  = array('cmd' => 'tar', 'argc' => '-cf', 'ext' => 'tar');
4144
				$arcs['extract']['application/x-tar'] = array('cmd' => 'tar', 'argc' => '-xf', 'ext' => 'tar');
4145
				unset($o);
4146
				$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...
4147 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...
4148
					$arcs['create']['application/x-gzip']  = array('cmd' => 'tar', 'argc' => '-czf', 'ext' => 'tgz');
4149
					$arcs['extract']['application/x-gzip'] = array('cmd' => 'tar', 'argc' => '-xzf', 'ext' => 'tgz');
4150
				}
4151
				unset($o);
4152
				$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...
4153 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...
4154
					$arcs['create']['application/x-bzip2']  = array('cmd' => 'tar', 'argc' => '-cjf', 'ext' => 'tbz');
4155
					$arcs['extract']['application/x-bzip2'] = array('cmd' => 'tar', 'argc' => '-xjf', 'ext' => 'tbz');
4156
				}
4157
				unset($o);
4158
				$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...
4159 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...
4160
					$arcs['create']['application/x-xz']  = array('cmd' => 'tar', 'argc' => '-cJf', 'ext' => 'xz');
4161
					$arcs['extract']['application/x-xz'] = array('cmd' => 'tar', 'argc' => '-xJf', 'ext' => 'xz');
4162
				}
4163
			}
4164
			unset($o);
4165
			$this->procExec('zip -v', $o, $c);
4166
			if ($c == 0) {
4167
				$arcs['create']['application/zip']  = array('cmd' => 'zip', 'argc' => '-r9', 'ext' => 'zip');
4168
			}
4169
			unset($o);
4170
			$this->procExec('unzip --help', $o, $c);
4171
			if ($c == 0) {
4172
				$arcs['extract']['application/zip'] = array('cmd' => 'unzip', 'argc' => '',  'ext' => 'zip');
4173
			}
4174
			unset($o);
4175
			$this->procExec('rar --version', $o, $c);
4176
			if ($c == 0 || $c == 7) {
4177
				$arcs['create']['application/x-rar']  = array('cmd' => 'rar', 'argc' => 'a -inul', 'ext' => 'rar');
4178
				$arcs['extract']['application/x-rar'] = array('cmd' => 'rar', 'argc' => 'x -y',    'ext' => 'rar');
4179
			} else {
4180
				unset($o);
4181
				$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...
4182
				if ($c==0 || $c == 7) {
4183
					$arcs['extract']['application/x-rar'] = array('cmd' => 'unrar', 'argc' => 'x -y', 'ext' => 'rar');
4184
				}
4185
			}
4186
			unset($o);
4187
			$this->procExec('7za --help', $o, $c);
4188
			if ($c == 0) {
4189
				$arcs['create']['application/x-7z-compressed']  = array('cmd' => '7za', 'argc' => 'a', 'ext' => '7z');
4190
				$arcs['extract']['application/x-7z-compressed'] = array('cmd' => '7za', 'argc' => 'x -y', 'ext' => '7z');
4191
				
4192
				if (empty($arcs['create']['application/zip'])) {
4193
					$arcs['create']['application/zip'] = array('cmd' => '7za', 'argc' => 'a -tzip', 'ext' => 'zip');
4194
				}
4195 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...
4196
					$arcs['extract']['application/zip'] = array('cmd' => '7za', 'argc' => 'x -tzip -y', 'ext' => 'zip');
4197
				}
4198
				if (empty($arcs['create']['application/x-tar'])) {
4199
					$arcs['create']['application/x-tar'] = array('cmd' => '7za', 'argc' => 'a -ttar', 'ext' => 'tar');
4200
				}
4201 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...
4202
					$arcs['extract']['application/x-tar'] = array('cmd' => '7za', 'argc' => 'x -ttar -y', 'ext' => 'tar');
4203
				}
4204
			} else if (substr(PHP_OS,0,3) === 'WIN') {
4205
				// check `7z` for Windows server.
4206
				unset($o);
4207
				$this->procExec('7z', $o, $c);
4208
				if ($c == 0) {
4209
					$arcs['create']['application/x-7z-compressed']  = array('cmd' => '7z', 'argc' => 'a', 'ext' => '7z');
4210
					$arcs['extract']['application/x-7z-compressed'] = array('cmd' => '7z', 'argc' => 'x -y', 'ext' => '7z');
4211
					
4212
					if (empty($arcs['create']['application/zip'])) {
4213
						$arcs['create']['application/zip'] = array('cmd' => '7z', 'argc' => 'a -tzip', 'ext' => 'zip');
4214
					}
4215
					if (empty($arcs['extract']['application/zip'])) {
4216
						$arcs['extract']['application/zip'] = array('cmd' => '7z', 'argc' => 'x -tzip -y', 'ext' => 'zip');
4217
					}
4218
					if (empty($arcs['create']['application/x-tar'])) {
4219
						$arcs['create']['application/x-tar'] = array('cmd' => '7z', 'argc' => 'a -ttar', 'ext' => 'tar');
4220
					}
4221
					if (empty($arcs['extract']['application/x-tar'])) {
4222
						$arcs['extract']['application/x-tar'] = array('cmd' => '7z', 'argc' => 'x -ttar -y', 'ext' => 'tar');
4223
					}
4224
				}
4225
			}
4226
		
4227
		}
4228
		
4229
		// Use PHP ZipArchive Class
4230
		if (class_exists('ZipArchive', false)) {
4231
			if (empty($arcs['create']['application/zip'])) {
4232
				$arcs['create']['application/zip']  = array('cmd' => 'phpfunction', 'argc' => 'self::zipArchiveZip', 'ext' => 'zip');
4233
			}
4234 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...
4235
				$arcs['extract']['application/zip'] = array('cmd' => 'phpfunction', 'argc' => 'self::zipArchiveUnzip', 'ext' => 'zip');
4236
			}
4237
		}
4238
		
4239
		$this->sessionCache[$sessionKey] = $arcs;
4240
		return $arcs;
4241
	}
4242
4243
	/**
4244
	 * Resolve relative / (Unix-like)absolute path
4245
	 * 
4246
	 * @param string $path  target path
4247
	 * @param string $base  base path
4248
	 * @return string
4249
	 */
4250
	protected function getFullPath($path, $base) {
4251
		$separator = $this->separator;
4252
		$systemroot = $this->systemRoot;
4253
4254
		$sepquoted = preg_quote($separator, '#');
4255
4256
		// normalize `/../`
4257
		$normreg = '#('.$sepquoted.')[^'.$sepquoted.']+'.$sepquoted.'\.\.'.$sepquoted.'#';
4258
		while(preg_match($normreg, $path)) {
4259
			$path = preg_replace($normreg, '$1', $path);
4260
		}
4261
		
4262
		// 'Here'
4263
		if ($path === '' || $path === '.' . $separator) return $base;
4264
		
4265
		// Absolute path
4266
		if ($path[0] === $separator || strpos($path, $systemroot) === 0) {
4267
			return $path;
4268
		}
4269
		
4270
		$preg_separator = '#' . $sepquoted . '#';
4271
		
4272
		// Relative path from 'Here'
4273
		if (substr($path, 0, 2) === '.' . $separator || $path[0] !== '.' || substr($path, 0, 3) !== '..' . $separator) {
4274
			$arrn = preg_split($preg_separator, $path, -1, PREG_SPLIT_NO_EMPTY);
4275
			if ($arrn[0] !== '.') {
4276
				array_unshift($arrn, '.');
4277
			}
4278
			$arrn[0] = $base;
4279
			return join($separator, $arrn);
4280
		}
4281
		
4282
		// Relative path from dirname()
4283
		if (substr($path, 0, 3) === '../') {
4284
			$arrn = preg_split($preg_separator, $path, -1, PREG_SPLIT_NO_EMPTY);
4285
			$arrp = preg_split($preg_separator, $base, -1, PREG_SPLIT_NO_EMPTY);
4286
		
4287
			while (! empty($arrn) && $arrn[0] === '..') {
4288
				array_shift($arrn);
4289
				array_pop($arrp);
4290
			}
4291
			$path = ! empty($arrp) ? $systemroot . join($separator, array_merge($arrp, $arrn)) :
4292
				(! empty($arrn) ? $systemroot . join($separator, $arrn) : $systemroot);
4293
		}
4294
		
4295
		return $path;
4296
	}
4297
4298
	/**
4299
	 * Remove directory recursive on local file system
4300
	 *
4301
	 * @param string $dir Target dirctory path
4302
	 * @return boolean
4303
	 * @author Naoki Sawada
4304
	 */
4305
	public function rmdirRecursive($dir) {
4306
		if (!is_link($dir) && is_dir($dir)) {
4307
			@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...
4308
			foreach (array_diff(scandir($dir), array('.', '..')) as $file) {
4309
				@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...
4310
				$path = $dir . DIRECTORY_SEPARATOR . $file;
4311
				if (!is_link($dir) && is_dir($path)) {
4312
					$this->rmdirRecursive($path);
4313
				} else {
4314
					@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...
4315
					@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...
4316
				}
4317
			}
4318
			return @rmdir($dir);
4319
		} else if (is_file($dir) || is_link($dir)) {
4320
			@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...
4321
			return @unlink($dir);
4322
		}
4323
		return false;
4324
	}
4325
4326
	/**
4327
	 * Create archive and return its path
4328
	 *
4329
	 * @param  string  $dir    target dir
4330
	 * @param  array   $files  files names list
4331
	 * @param  string  $name   archive name
4332
	 * @param  array   $arc    archiver options
4333
	 * @return string|bool
4334
	 * @author Dmitry (dio) Levashov, 
4335
	 * @author Alexey Sukhotin
4336
	 * @author Naoki Sawada
4337
	 **/
4338
	protected function makeArchive($dir, $files, $name, $arc) {
4339
		if ($arc['cmd'] === 'phpfunction') {
4340 View Code Duplication
			if (is_callable($arc['argc'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
4341
				call_user_func_array($arc['argc'], array($dir, $files, $name));
4342
			}
4343
		} else {
4344
			$cwd = getcwd();
4345
			chdir($dir);
4346
			
4347
			$files = array_map('escapeshellarg', $files);
4348
			
4349
			$cmd = $arc['cmd'].' '.$arc['argc'].' '.escapeshellarg($name).' '.implode(' ', $files);
4350
			$this->procExec($cmd, $o, $c);
4351
			chdir($cwd);
4352
		}
4353
		$path = $dir.DIRECTORY_SEPARATOR.$name;
4354
		return file_exists($path) ? $path : false;
4355
	}
4356
	
4357
	/**
4358
	 * Unpack archive
4359
	 *
4360
	 * @param  string  $path   archive path
4361
	 * @param  array   $arc    archiver command and arguments (same as in $this->archivers)
4362
	 * @param  bool    $remove remove archive ( unlink($path) )
4363
	 * @return void
4364
	 * @author Dmitry (dio) Levashov
4365
	 * @author Alexey Sukhotin
4366
	 * @author Naoki Sawada
4367
	 **/
4368
	protected function unpackArchive($path, $arc, $remove = true) {
4369
		$dir = dirname($path);
4370
		if ($arc['cmd'] === 'phpfunction') {
4371 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...
4372
				call_user_func_array($arc['argc'], array($path, $dir));
4373
			}
4374
		} else {
4375
			$cwd = getcwd();
4376
			chdir($dir);
4377
			$cmd = $arc['cmd'].' '.$arc['argc'].' '.escapeshellarg(basename($path));
4378
			$this->procExec($cmd, $o, $c);
4379
			chdir($cwd);
4380
		}
4381
		$remove && unlink($path);
4382
	}
4383
	
4384
	/**
4385
	 * Create Zip archive using PHP class ZipArchive
4386
	 * 
4387
	 * @param  string        $dir      target dir
4388
	 * @param  array         $files    files names list
4389
	 * @param  string|object $zipPath  Zip archive name
4390
	 * @return void
4391
	 * @author Naoki Sawada
4392
	 */
4393
	protected static function zipArchiveZip($dir, $files, $zipPath) {
4394
		try {
4395
			if ($start = is_string($zipPath)) {
4396
				$zip = new ZipArchive();
4397
				if ($zip->open($dir . DIRECTORY_SEPARATOR . $zipPath, ZipArchive::CREATE) !== true) {
4398
					$zip = false;
4399
				}
4400
			} else {
4401
				$zip = $zipPath;
4402
			}
4403
			if ($zip) {
4404
				foreach($files as $file) {
4405
					$path = $dir . DIRECTORY_SEPARATOR . $file;
4406
					if (is_dir($path)) {
4407
						$zip->addEmptyDir($file);
4408
						$_files = array();
4409
						if ($handle = opendir($path)) {
4410
							while (false !== ($entry = readdir($handle))) {
4411
								if ($entry !== "." && $entry !== "..") {
4412
									$_files[] = $file . DIRECTORY_SEPARATOR . $entry;
4413
								}
4414
							}
4415
							closedir($handle);
4416
						}
4417
						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...
4418
							self::zipArchiveZip($dir, $_files, $zip);
4419
						}
4420
					} else {
4421
						$zip->addFile($path, $file);
4422
					}
4423
				}
4424
				$start && $zip->close();
4425
			}
4426
		} catch (Exception $e) {
4427
			return false;
4428
		}
4429
		return true;
4430
	}
4431
	
4432
	/**
4433
	 * Unpack Zip archive using PHP class ZipArchive
4434
	 * 
4435
	 * @param  string $zipPath  Zip archive name
4436
	 * @param  string $toDir    Extract to path
4437
	 * @return bool
4438
	 * @author Naoki Sawada
4439
	 */
4440
	protected static function zipArchiveUnzip($zipPath, $toDir) {
4441
		try {
4442
			$zip = new ZipArchive();
4443
			if ($zip->open($zipPath) === true) {
4444
				$zip->extractTo($toDir);
4445
				$zip->close();
4446
			}
4447
		} catch (Exception $e) {
4448
			return false;
4449
		}
4450
		return true;
4451
	}
4452
	
4453
	/**==================================* abstract methods *====================================**/
4454
	
4455
	/*********************** paths/urls *************************/
4456
	
4457
	/**
4458
	 * Return parent directory path
4459
	 *
4460
	 * @param  string  $path  file path
4461
	 * @return string
4462
	 * @author Dmitry (dio) Levashov
4463
	 **/
4464
	abstract protected function _dirname($path);
4465
4466
	/**
4467
	 * Return file name
4468
	 *
4469
	 * @param  string  $path  file path
4470
	 * @return string
4471
	 * @author Dmitry (dio) Levashov
4472
	 **/
4473
	abstract protected function _basename($path);
4474
4475
	/**
4476
	 * Join dir name and file name and return full path.
4477
	 * Some drivers (db) use int as path - so we give to concat path to driver itself
4478
	 *
4479
	 * @param  string  $dir   dir path
4480
	 * @param  string  $name  file name
4481
	 * @return string
4482
	 * @author Dmitry (dio) Levashov
4483
	 **/
4484
	abstract protected function _joinPath($dir, $name);
4485
4486
	/**
4487
	 * Return normalized path 
4488
	 *
4489
	 * @param  string  $path  file path
4490
	 * @return string
4491
	 * @author Dmitry (dio) Levashov
4492
	 **/
4493
	abstract protected function _normpath($path);
4494
4495
	/**
4496
	 * Return file path related to root dir
4497
	 *
4498
	 * @param  string  $path  file path
4499
	 * @return string
4500
	 * @author Dmitry (dio) Levashov
4501
	 **/
4502
	abstract protected function _relpath($path);
4503
	
4504
	/**
4505
	 * Convert path related to root dir into real path
4506
	 *
4507
	 * @param  string  $path  rel file path
4508
	 * @return string
4509
	 * @author Dmitry (dio) Levashov
4510
	 **/
4511
	abstract protected function _abspath($path);
4512
	
4513
	/**
4514
	 * Return fake path started from root dir.
4515
	 * Required to show path on client side.
4516
	 *
4517
	 * @param  string  $path  file path
4518
	 * @return string
4519
	 * @author Dmitry (dio) Levashov
4520
	 **/
4521
	abstract protected function _path($path);
4522
	
4523
	/**
4524
	 * Return true if $path is children of $parent
4525
	 *
4526
	 * @param  string  $path    path to check
4527
	 * @param  string  $parent  parent path
4528
	 * @return bool
4529
	 * @author Dmitry (dio) Levashov
4530
	 **/
4531
	abstract protected function _inpath($path, $parent);
4532
	
4533
	/**
4534
	 * Return stat for given path.
4535
	 * Stat contains following fields:
4536
	 * - (int)    size    file size in b. required
4537
	 * - (int)    ts      file modification time in unix time. required
4538
	 * - (string) mime    mimetype. required for folders, others - optionally
4539
	 * - (bool)   read    read permissions. required
4540
	 * - (bool)   write   write permissions. required
4541
	 * - (bool)   locked  is object locked. optionally
4542
	 * - (bool)   hidden  is object hidden. optionally
4543
	 * - (string) alias   for symlinks - link target path relative to root path. optionally
4544
	 * - (string) target  for symlinks - link target path. optionally
4545
	 *
4546
	 * If file does not exists - returns empty array or false.
4547
	 *
4548
	 * @param  string  $path    file path 
4549
	 * @return array|false
4550
	 * @author Dmitry (dio) Levashov
4551
	 **/
4552
	abstract protected function _stat($path);
4553
	
4554
4555
	/***************** file stat ********************/
4556
4557
		
4558
	/**
4559
	 * Return true if path is dir and has at least one childs directory
4560
	 *
4561
	 * @param  string  $path  dir path
4562
	 * @return bool
4563
	 * @author Dmitry (dio) Levashov
4564
	 **/
4565
	abstract protected function _subdirs($path);
4566
	
4567
	/**
4568
	 * Return object width and height
4569
	 * Ususaly used for images, but can be realize for video etc...
4570
	 *
4571
	 * @param  string  $path  file path
4572
	 * @param  string  $mime  file mime type
4573
	 * @return string
4574
	 * @author Dmitry (dio) Levashov
4575
	 **/
4576
	abstract protected function _dimensions($path, $mime);
4577
	
4578
	/******************** file/dir content *********************/
4579
4580
	/**
4581
	 * Return files list in directory
4582
	 *
4583
	 * @param  string  $path  dir path
4584
	 * @return array
4585
	 * @author Dmitry (dio) Levashov
4586
	 **/
4587
	abstract protected function _scandir($path);
4588
	
4589
	/**
4590
	 * Open file and return file pointer
4591
	 *
4592
	 * @param  string  $path  file path
4593
	 * @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...
4594
	 * @return resource|false
4595
	 * @author Dmitry (dio) Levashov
4596
	 **/
4597
	abstract protected function _fopen($path, $mode="rb");
4598
	
4599
	/**
4600
	 * Close opened file
4601
	 * 
4602
	 * @param  resource  $fp    file pointer
4603
	 * @param  string    $path  file path
4604
	 * @return bool
4605
	 * @author Dmitry (dio) Levashov
4606
	 **/
4607
	abstract protected function _fclose($fp, $path='');
4608
	
4609
	/********************  file/dir manipulations *************************/
4610
	
4611
	/**
4612
	 * Create dir and return created dir path or false on failed
4613
	 *
4614
	 * @param  string  $path  parent dir path
4615
	 * @param string  $name  new directory name
4616
	 * @return string|bool
4617
	 * @author Dmitry (dio) Levashov
4618
	 **/
4619
	abstract protected function _mkdir($path, $name);
4620
	
4621
	/**
4622
	 * Create file and return it's path or false on failed
4623
	 *
4624
	 * @param  string  $path  parent dir path
4625
	 * @param string  $name  new file name
4626
	 * @return string|bool
4627
	 * @author Dmitry (dio) Levashov
4628
	 **/
4629
	abstract protected function _mkfile($path, $name);
4630
	
4631
	/**
4632
	 * Create symlink
4633
	 *
4634
	 * @param  string  $source     file to link to
4635
	 * @param  string  $targetDir  folder to create link in
4636
	 * @param  string  $name       symlink name
4637
	 * @return bool
4638
	 * @author Dmitry (dio) Levashov
4639
	 **/
4640
	abstract protected function _symlink($source, $targetDir, $name);
4641
	
4642
	/**
4643
	 * Copy file into another file (only inside one volume)
4644
	 *
4645
	 * @param  string  $source  source file path
4646
	 * @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...
4647
	 * @param  string  $name    file name
4648
	 * @return bool
4649
	 * @author Dmitry (dio) Levashov
4650
	 **/
4651
	abstract protected function _copy($source, $targetDir, $name);
4652
	
4653
	/**
4654
	 * Move file into another parent dir.
4655
	 * Return new file path or false.
4656
	 *
4657
	 * @param  string  $source  source file path
4658
	 * @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...
4659
	 * @param  string  $name    file name
4660
	 * @return string|bool
4661
	 * @author Dmitry (dio) Levashov
4662
	 **/
4663
	abstract protected function _move($source, $targetDir, $name);
4664
	
4665
	/**
4666
	 * Remove file
4667
	 *
4668
	 * @param  string  $path  file path
4669
	 * @return bool
4670
	 * @author Dmitry (dio) Levashov
4671
	 **/
4672
	abstract protected function _unlink($path);
4673
4674
	/**
4675
	 * Remove dir
4676
	 *
4677
	 * @param  string  $path  dir path
4678
	 * @return bool
4679
	 * @author Dmitry (dio) Levashov
4680
	 **/
4681
	abstract protected function _rmdir($path);
4682
4683
	/**
4684
	 * Create new file and write into it from file pointer.
4685
	 * Return new file path or false on error.
4686
	 *
4687
	 * @param  resource  $fp   file pointer
4688
	 * @param  string    $dir  target dir path
4689
	 * @param  string    $name file name
4690
	 * @param  array     $stat file stat (required by some virtual fs)
4691
	 * @return bool|string
4692
	 * @author Dmitry (dio) Levashov
4693
	 **/
4694
	abstract protected function _save($fp, $dir, $name, $stat);
4695
	
4696
	/**
4697
	 * Get file contents
4698
	 *
4699
	 * @param  string  $path  file path
4700
	 * @return string|false
4701
	 * @author Dmitry (dio) Levashov
4702
	 **/
4703
	abstract protected function _getContents($path);
4704
	
4705
	/**
4706
	 * Write a string to a file
4707
	 *
4708
	 * @param  string  $path     file path
4709
	 * @param  string  $content  new file content
4710
	 * @return bool
4711
	 * @author Dmitry (dio) Levashov
4712
	 **/
4713
	abstract protected function _filePutContents($path, $content);
4714
4715
	/**
4716
	 * Extract files from archive
4717
	 *
4718
	 * @param  string  $path file path
4719
	 * @param  array   $arc  archiver options
4720
	 * @return bool
4721
	 * @author Dmitry (dio) Levashov, 
4722
	 * @author Alexey Sukhotin
4723
	 **/
4724
	abstract protected function _extract($path, $arc);
4725
4726
	/**
4727
	 * Create archive and return its path
4728
	 *
4729
	 * @param  string  $dir    target dir
4730
	 * @param  array   $files  files names list
4731
	 * @param  string  $name   archive name
4732
	 * @param  array   $arc    archiver options
4733
	 * @return string|bool
4734
	 * @author Dmitry (dio) Levashov, 
4735
	 * @author Alexey Sukhotin
4736
	 **/
4737
	abstract protected function _archive($dir, $files, $name, $arc);
4738
4739
	/**
4740
	 * Detect available archivers
4741
	 *
4742
	 * @return void
4743
	 * @author Dmitry (dio) Levashov, 
4744
	 * @author Alexey Sukhotin
4745
	 **/
4746
	abstract protected function _checkArchivers();
4747
4748
	/**
4749
	 * Change file mode (chmod)
4750
	 *
4751
	 * @param  string  $path  file path
4752
	 * @param  string  $mode  octal string such as '0755'
4753
	 * @return bool
4754
	 * @author David Bartle,
4755
	 **/
4756
	abstract protected function _chmod($path, $mode);
4757
4758
	
4759
} // END class
4760