Completed
Push — 2.x ( 03af2a...1b6e15 )
by Naoki
07:15
created

elFinder::search()   C

Complexity

Conditions 8
Paths 48

Size

Total Lines 25
Code Lines 18

Duplication

Lines 10
Ratio 40 %
Metric Value
dl 10
loc 25
rs 5.3846
cc 8
eloc 18
nc 48
nop 1
1
<?php
2
3
/**
4
 * elFinder - file manager for web.
5
 * Core class.
6
 *
7
 * @package elfinder
8
 * @author Dmitry (dio) Levashov
9
 * @author Troex Nevelin
10
 * @author Alexey Sukhotin
11
 **/
12
class elFinder {
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...
13
	
14
	/**
15
	 * API version number
16
	 *
17
	 * @var string
18
	 **/
19
	protected $version = '2.0';
20
	
21
	/**
22
	 * Storages (root dirs)
23
	 *
24
	 * @var array
25
	 **/
26
	protected $volumes = array();
27
	
28
	/**
29
	 * Network mount drivers
30
	 * 
31
	 * @var array
32
	 */
33
	public static $netDrivers = array();
34
	
35
	/**
36
	 * elFinder global locale
37
	 * 
38
	 * @var string
39
	 */
40
	public static $locale = '';
41
	
42
	/**
43
	 * elFinder global sessionCacheKey
44
	 * 
45
	 * @var string
46
	 */
47
	public static $sessionCacheKey = '';
48
	
49
	/**
50
	 * elFinder base64encodeSessionData
51
	 * elFinder save session data as `UTF-8`
52
	 * If the session storage mechanism of the system does not allow `UTF-8`
53
	 * And it must be `true` option 'base64encodeSessionData' of elFinder
54
	 * 
55
	 * @var bool
56
	 */
57
	protected static $base64encodeSessionData = false;
58
	
59
	/**
60
	 * Session key of net mount volumes
61
	 * @var string
62
	 */
63
	protected $netVolumesSessionKey = '';
64
	
65
	/**
66
	 * Mounted volumes count
67
	 * Required to create unique volume id
68
	 *
69
	 * @var int
70
	 **/
71
	public static $volumesCnt = 1;
72
	
73
	/**
74
	 * Default root (storage)
75
	 *
76
	 * @var elFinderStorageDriver
77
	 **/
78
	protected $default = null;
79
	
80
	/**
81
	 * Commands and required arguments list
82
	 *
83
	 * @var array
84
	 **/
85
	protected $commands = array(
86
		'open'      => array('target' => false, 'tree' => false, 'init' => false, 'mimes' => false, 'compare' => false),
87
		'ls'        => array('target' => true, 'mimes' => false),
88
		'tree'      => array('target' => true),
89
		'parents'   => array('target' => true),
90
		'tmb'       => array('targets' => true),
91
		'file'      => array('target' => true, 'download' => false),
92
		'size'      => array('targets' => true),
93
		'mkdir'     => array('target' => true, 'name' => true),
94
		'mkfile'    => array('target' => true, 'name' => true, 'mimes' => false),
95
		'rm'        => array('targets' => true),
96
		'rename'    => array('target' => true, 'name' => true, 'mimes' => false),
97
		'duplicate' => array('targets' => true, 'suffix' => false),
98
		'paste'     => array('dst' => true, 'targets' => true, 'cut' => false, 'mimes' => false, 'renames' => false, 'suffix' => false),
99
		'upload'    => array('target' => true, 'FILES' => true, 'mimes' => false, 'html' => false, 'upload' => false, 'name' => false, 'upload_path' => false, 'chunk' => false, 'cid' => false, 'node' => false, 'renames' => false, 'suffix' => false),
100
		'get'       => array('target' => true, 'conv' => false),
101
		'put'       => array('target' => true, 'content' => '', 'mimes' => false),
102
		'archive'   => array('targets' => true, 'type' => true, 'mimes' => false, 'name' => false),
103
		'extract'   => array('target' => true, 'mimes' => false, 'makedir' => false),
104
		'search'    => array('q' => true, 'mimes' => false, 'target' => false),
105
		'info'      => array('targets' => true, 'compare' => false),
106
		'dim'       => array('target' => true),
107
		'resize'    => array('target' => true, 'width' => true, 'height' => true, 'mode' => false, 'x' => false, 'y' => false, 'degree' => false, 'quality' => false),
108
		'netmount'  => array('protocol' => true, 'host' => true, 'path' => false, 'port' => false, 'user' => false, 'pass' => false, 'alias' => false, 'options' => false),
109
		'url'       => array('target' => true, 'options' => false),
110
		'callback'  => array('node' => true, 'json' => false, 'bind' => false, 'done' => false),
111
		'chmod'     => array('targets' => true, 'mode' => true)
112
	);
113
	
114
	/**
115
	 * Plugins instance
116
	 *
117
	 * @var array
118
	 **/
119
	protected $plugins = array();
120
	
121
	/**
122
	 * Commands listeners
123
	 *
124
	 * @var array
125
	 **/
126
	protected $listeners = array();
127
	
128
	/**
129
	 * script work time for debug
130
	 *
131
	 * @var string
132
	 **/
133
	protected $time = 0;
134
	/**
135
	 * Is elFinder init correctly?
136
	 *
137
	 * @var bool
138
	 **/
139
	protected $loaded = false;
140
	/**
141
	 * Send debug to client?
142
	 *
143
	 * @var string
144
	 **/
145
	protected $debug = false;
146
	
147
	/**
148
	 * Call `session_write_close()` before exec command?
149
	 * 
150
	 * @var bool
151
	 */
152
	protected $sessionCloseEarlier = true;
153
154
	/**
155
	 * SESSION use commands @see __construct()
156
	 * 
157
	 * @var array
158
	 */
159
	protected $sessionUseCmds = array();
160
	
161
	/**
162
	 * session expires timeout
163
	 *
164
	 * @var int
165
	 **/
166
	protected $timeout = 0;
167
	
168
	/**
169
	 * Temp dir path for Upload
170
	 * 
171
	 * @var string
172
	 */
173
	protected $uploadTempPath = '';
174
	
175
	/**
176
	 * undocumented class variable
177
	 *
178
	 * @var string
179
	 **/
180
	protected $uploadDebug = '';
181
	
182
	/**
183
	 * Errors from not mounted volumes
184
	 *
185
	 * @var array
186
	 **/
187
	public $mountErrors = array();
188
	
189
	/**
190
	 * URL for callback output window for CORS
191
	 * redirect to this URL when callback output
192
	 * 
193
	 * @var string URL
194
	 */
195
	protected $callbackWindowURL = '';
196
	
197
	// Errors messages
198
	const ERROR_UNKNOWN           = 'errUnknown';
199
	const ERROR_UNKNOWN_CMD       = 'errUnknownCmd';
200
	const ERROR_CONF              = 'errConf';
201
	const ERROR_CONF_NO_JSON      = 'errJSON';
202
	const ERROR_CONF_NO_VOL       = 'errNoVolumes';
203
	const ERROR_INV_PARAMS        = 'errCmdParams';
204
	const ERROR_OPEN              = 'errOpen';
205
	const ERROR_DIR_NOT_FOUND     = 'errFolderNotFound';
206
	const ERROR_FILE_NOT_FOUND    = 'errFileNotFound';     // 'File not found.'
207
	const ERROR_TRGDIR_NOT_FOUND  = 'errTrgFolderNotFound'; // 'Target folder "$1" not found.'
208
	const ERROR_NOT_DIR           = 'errNotFolder';
209
	const ERROR_NOT_FILE          = 'errNotFile';
210
	const ERROR_PERM_DENIED       = 'errPerm';
211
	const ERROR_LOCKED            = 'errLocked';        // '"$1" is locked and can not be renamed, moved or removed.'
212
	const ERROR_EXISTS            = 'errExists';        // 'File named "$1" already exists.'
213
	const ERROR_INVALID_NAME      = 'errInvName';       // 'Invalid file name.'
214
	const ERROR_MKDIR             = 'errMkdir';
215
	const ERROR_MKFILE            = 'errMkfile';
216
	const ERROR_RENAME            = 'errRename';
217
	const ERROR_COPY              = 'errCopy';
218
	const ERROR_MOVE              = 'errMove';
219
	const ERROR_COPY_FROM         = 'errCopyFrom';
220
	const ERROR_COPY_TO           = 'errCopyTo';
221
	const ERROR_COPY_ITSELF       = 'errCopyInItself';
222
	const ERROR_REPLACE           = 'errReplace';          // 'Unable to replace "$1".'
223
	const ERROR_RM                = 'errRm';               // 'Unable to remove "$1".'
224
	const ERROR_RM_SRC            = 'errRmSrc';            // 'Unable remove source file(s)'
225
	const ERROR_MKOUTLINK         = 'errMkOutLink';        // 'Unable to create a link to outside the volume root.'
226
	const ERROR_UPLOAD            = 'errUpload';           // 'Upload error.'
227
	const ERROR_UPLOAD_FILE       = 'errUploadFile';       // 'Unable to upload "$1".'
228
	const ERROR_UPLOAD_NO_FILES   = 'errUploadNoFiles';    // 'No files found for upload.'
229
	const ERROR_UPLOAD_TOTAL_SIZE = 'errUploadTotalSize';  // 'Data exceeds the maximum allowed size.'
230
	const ERROR_UPLOAD_FILE_SIZE  = 'errUploadFileSize';   // 'File exceeds maximum allowed size.'
231
	const ERROR_UPLOAD_FILE_MIME  = 'errUploadMime';       // 'File type not allowed.'
232
	const ERROR_UPLOAD_TRANSFER   = 'errUploadTransfer';   // '"$1" transfer error.'
233
	const ERROR_UPLOAD_TEMP       = 'errUploadTemp';       // 'Unable to make temporary file for upload.'
234
	// const ERROR_ACCESS_DENIED     = 'errAccess';
235
	const ERROR_NOT_REPLACE       = 'errNotReplace';       // Object "$1" already exists at this location and can not be replaced with object of another type.
236
	const ERROR_SAVE              = 'errSave';
237
	const ERROR_EXTRACT           = 'errExtract';
238
	const ERROR_ARCHIVE           = 'errArchive';
239
	const ERROR_NOT_ARCHIVE       = 'errNoArchive';
240
	const ERROR_ARCHIVE_TYPE      = 'errArcType';
241
	const ERROR_ARC_SYMLINKS      = 'errArcSymlinks';
242
	const ERROR_ARC_MAXSIZE       = 'errArcMaxSize';
243
	const ERROR_RESIZE            = 'errResize';
244
	const ERROR_UNSUPPORT_TYPE    = 'errUsupportType';
245
	const ERROR_CONV_UTF8         = 'errConvUTF8';
246
	const ERROR_NOT_UTF8_CONTENT  = 'errNotUTF8Content';
247
	const ERROR_NETMOUNT          = 'errNetMount';
248
	const ERROR_NETUNMOUNT        = 'errNetUnMount';
249
	const ERROR_NETMOUNT_NO_DRIVER = 'errNetMountNoDriver';
250
	const ERROR_NETMOUNT_FAILED       = 'errNetMountFailed';
251
252
	const ERROR_SESSION_EXPIRES 	= 'errSessionExpires';
253
254
	const ERROR_CREATING_TEMP_DIR 	= 'errCreatingTempDir';
255
	const ERROR_FTP_DOWNLOAD_FILE 	= 'errFtpDownloadFile';
256
	const ERROR_FTP_UPLOAD_FILE 	= 'errFtpUploadFile';
257
	const ERROR_FTP_MKDIR 		= 'errFtpMkdir';
258
	const ERROR_ARCHIVE_EXEC 	= 'errArchiveExec';
259
	const ERROR_EXTRACT_EXEC 	= 'errExtractExec';
260
	const ERROR_SEARCH_TIMEOUT    = 'errSearchTimeout';    // 'Timed out while searching "$1". Search result is partial.'
261
262
	/**
263
	 * Constructor
264
	 *
265
	 * @param  array  elFinder and roots configurations
266
	 * @return void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
267
	 * @author Dmitry (dio) Levashov
268
	 **/
269
	public function __construct($opts) {
0 ignored issues
show
Coding Style introduced by
__construct 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
__construct 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...
Coding Style introduced by
__construct 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
__construct 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...
270
		// try session start | restart
271
		try {
272
			session_start();
273
		} catch (Exception $e) {}
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
274
		
275
		$sessionUseCmds = array();
276
		if (isset($opts['sessionUseCmds']) && is_array($opts['sessionUseCmds'])) {
277
			$sessionUseCmds = $opts['sessionUseCmds'];
278
		}
279
280
		// set self::$volumesCnt by HTTP header "X-elFinder-VolumesCntStart"
281
		if (isset($_SERVER['HTTP_X_ELFINDER_VOLUMESCNTSTART']) && ($volumesCntStart = intval($_SERVER['HTTP_X_ELFINDER_VOLUMESCNTSTART']))) {
282
			self::$volumesCnt = $volumesCntStart;
283
		}
284
		
285
		$this->time  = $this->utime();
0 ignored issues
show
Documentation Bug introduced by
The property $time was declared of type string, but $this->utime() is of type double. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
286
		$this->debug = (isset($opts['debug']) && $opts['debug'] ? true : false);
0 ignored issues
show
Documentation Bug introduced by
The property $debug was declared of type string, but isset($opts['debug']) &&...'debug'] ? true : false is of type boolean. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
287
		$this->sessionCloseEarlier = isset($opts['sessionCloseEarlier'])? (bool)$opts['sessionCloseEarlier'] : true;
288
		$this->sessionUseCmds = array_flip($sessionUseCmds);
289
		$this->timeout = (isset($opts['timeout']) ? $opts['timeout'] : 0);
290
		$this->uploadTempPath = (isset($opts['uploadTempPath']) ? $opts['uploadTempPath'] : '');
291
		$this->netVolumesSessionKey = !empty($opts['netVolumesSessionKey'])? $opts['netVolumesSessionKey'] : 'elFinderNetVolumes';
292
		$this->callbackWindowURL = (isset($opts['callbackWindowURL']) ? $opts['callbackWindowURL'] : '');
293
		self::$sessionCacheKey = !empty($opts['sessionCacheKey']) ? $opts['sessionCacheKey'] : 'elFinderCaches';
294
		
295
		// check session cache
296
		$_optsMD5 = md5(json_encode($opts['roots']));
297
		if (! isset($_SESSION[self::$sessionCacheKey]) || $_SESSION[self::$sessionCacheKey]['_optsMD5'] !== $_optsMD5) {
298
			$_SESSION[self::$sessionCacheKey] = array(
299
				'_optsMD5' => $_optsMD5
300
			);
301
		}
302
		self::$base64encodeSessionData = !empty($opts['base64encodeSessionData']);
303
		
304
		// setlocale and global locale regists to elFinder::locale
305
		self::$locale = !empty($opts['locale']) ? $opts['locale'] : 'en_US.UTF-8';
306
		if (false === @setlocale(LC_ALL, self::$locale)) {
307
			self::$locale = setlocale(LC_ALL, '');
308
		}
309
310
		// bind events listeners
311
		if (!empty($opts['bind']) && is_array($opts['bind'])) {
312
			$_req = $_SERVER["REQUEST_METHOD"] == 'POST' ? $_POST : $_GET;
313
			$_reqCmd = isset($_req['cmd']) ? $_req['cmd'] : '';
314
			foreach ($opts['bind'] as $cmd => $handlers) {
315
				$doRegist = (strpos($cmd, '*') !== false);
316
				if (! $doRegist) {
317
					$_getcmd = create_function('$cmd', 'list($ret) = explode(\'.\', $cmd);return trim($ret);');
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...
318
					$doRegist = ($_reqCmd && in_array($_reqCmd, array_map($_getcmd, explode(' ', $cmd))));
319
				}
320
				if ($doRegist) {
321
					if (! is_array($handlers) || is_object($handlers[0])) {
322
						$handlers = array($handlers);
323
					}
324
					foreach($handlers as $handler) {
325
						if ($handler) {
326
							if (is_string($handler) && strpos($handler, '.')) {
327
								list($_domain, $_name, $_method) = array_pad(explode('.', $handler), 3, '');
328
								if (strcasecmp($_domain, 'plugin') === 0) {
329
									if ($plugin = $this->getPluginInstance($_name, isset($opts['plugin'][$_name])? $opts['plugin'][$_name] : array())
330
											and method_exists($plugin, $_method)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
331
										$this->bind($cmd, array($plugin, $_method));
332
									}
333
								}
334
							} else {
335
								$this->bind($cmd, $handler);
336
							}
337
						}
338
					}
339
				}
340
			}
341
		}
342
343
		if (!isset($opts['roots']) || !is_array($opts['roots'])) {
344
			$opts['roots'] = array();
345
		}
346
347
		// check for net volumes stored in session
348
		foreach ($this->getNetVolumes() as $key => $root) {
349
			$opts['roots'][$key] = $root;
350
		}
351
352
		// "mount" volumes
353
		foreach ($opts['roots'] as $i => $o) {
354
			$class = 'elFinderVolume'.(isset($o['driver']) ? $o['driver'] : '');
355
356
			if (class_exists($class)) {
357
				$volume = new $class();
358
359
				try {
360
					if ($volume->mount($o)) {
361
						// unique volume id (ends on "_") - used as prefix to files hash
362
						$id = $volume->id();
363
						
364
						$this->volumes[$id] = $volume;
365
						if ((!$this->default || $volume->root() !== $volume->defaultPath()) && $volume->isReadable()) {
366
							$this->default = $this->volumes[$id]; 
367
						}
368
					} else {
369
						$this->removeNetVolume($i);
370
						$this->mountErrors[] = 'Driver "'.$class.'" : '.implode(' ', $volume->error());
371
					}
372
				} catch (Exception $e) {
373
					$this->removeNetVolume($i);
374
					$this->mountErrors[] = 'Driver "'.$class.'" : '.$e->getMessage();
375
				}
376
			} else {
377
				$this->mountErrors[] = 'Driver "'.$class.'" does not exists';
378
			}
379
		}
380
381
		// if at least one readable volume - ii desu >_<
382
		$this->loaded = !empty($this->default);
383
	}
384
	
385
	/**
386
	 * Return true if fm init correctly
387
	 *
388
	 * @return bool
389
	 * @author Dmitry (dio) Levashov
390
	 **/
391
	public function loaded() {
392
		return $this->loaded;
393
	}
394
	
395
	/**
396
	 * Return version (api) number
397
	 *
398
	 * @return string
399
	 * @author Dmitry (dio) Levashov
400
	 **/
401
	public function version() {
402
		return $this->version;
403
	}
404
	
405
	/**
406
	 * Add handler to elFinder command
407
	 *
408
	 * @param  string  command name
409
	 * @param  string|array  callback name or array(object, method)
410
	 * @return elFinder
411
	 * @author Dmitry (dio) Levashov
412
	 **/
413
	public function bind($cmd, $handler) {
414
		$allCmds = array_keys($this->commands);
415
		$cmds = array();
416
		foreach(explode(' ', $cmd) as $_cmd) {
417
			if ($_cmd !== '') {
418
				if ($all = strpos($_cmd, '*') !== false) {
0 ignored issues
show
Unused Code introduced by
$all 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...
419
					list(, $sub) = array_pad(explode('.', $_cmd), 2, '');
420
					if ($sub) {
421
						$sub = str_replace('\'', '\\\'', $sub);
422
						$addSub = create_function('$cmd', 'return $cmd . \'.\' . trim(\'' . $sub . '\');');
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...
423
						$cmds = array_merge($cmds, array_map($addSub, $allCmds));
424
					} else {
425
						$cmds = array_merge($cmds, $allCmds);
426
					}
427
				} else {
428
					$cmds[] = $_cmd;
429
				}
430
			}
431
		}
432
		$cmds = array_unique($cmds);
433
		
434
		foreach ($cmds as $cmd) {
435
			if (!isset($this->listeners[$cmd])) {
436
				$this->listeners[$cmd] = array();
437
			}
438
439
			if (is_callable($handler)) {
440
				$this->listeners[$cmd][] = $handler;
441
			}
442
		}
443
444
		return $this;
445
	}
446
	
447
	/**
448
	 * Remove event (command exec) handler
449
	 *
450
	 * @param  string  command name
451
	 * @param  string|array  callback name or array(object, method)
452
	 * @return elFinder
453
	 * @author Dmitry (dio) Levashov
454
	 **/
455
	public function unbind($cmd, $handler) {
456
		if (!empty($this->listeners[$cmd])) {
457
			foreach ($this->listeners[$cmd] as $i => $h) {
458
				if ($h === $handler) {
459
					unset($this->listeners[$cmd][$i]);
460
					return $this;
461
				}
462
			}
463
		}
464
		return $this;
465
	}
466
	
467
	/**
468
	 * Return true if command exists
469
	 *
470
	 * @param  string  command name
471
	 * @return bool
472
	 * @author Dmitry (dio) Levashov
473
	 **/
474
	public function commandExists($cmd) {
475
		return $this->loaded && isset($this->commands[$cmd]) && method_exists($this, $cmd);
476
	}
477
	
478
	/**
479
	 * Return root - file's owner (public func of volume())
480
	 *
481
	 * @param  string  file hash
482
	 * @return elFinderStorageDriver
483
	 * @author Naoki Sawada
484
	 */
485
	public function getVolume($hash) {
486
		return $this->volume($hash);
487
	}
488
	
489
	/**
490
	 * Return command required arguments info
491
	 *
492
	 * @param  string  command name
493
	 * @return array
494
	 * @author Dmitry (dio) Levashov
495
	 **/
496
	public function commandArgsList($cmd) {
497
		return $this->commandExists($cmd) ? $this->commands[$cmd] : array();
498
	}
499
500
	private function session_expires() {
0 ignored issues
show
Coding Style introduced by
session_expires 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...
501
		
502
		if (!isset($_SESSION[self::$sessionCacheKey . ':LAST_ACTIVITY'])) {
503
			$_SESSION[self::$sessionCacheKey . ':LAST_ACTIVITY'] = time();
504
			return false;
505
		}
506
507
		if ( ($this->timeout > 0) && (time() - $_SESSION[self::$sessionCacheKey . ':LAST_ACTIVITY'] > $this->timeout) ) {
508
			return true;
509
		}
510
511
		$_SESSION[self::$sessionCacheKey . ':LAST_ACTIVITY'] = time();
512
		return false;	
513
	}
514
	
515
	/**
516
	 * Exec command and return result
517
	 *
518
	 * @param  string  $cmd  command name
519
	 * @param  array   $args command arguments
520
	 * @return array
521
	 * @author Dmitry (dio) Levashov
522
	 **/
523
	public function exec($cmd, $args) {
524
		
525
		if (!$this->loaded) {
526
			return array('error' => $this->error(self::ERROR_CONF, self::ERROR_CONF_NO_VOL));
527
		}
528
529
		if ($this->session_expires()) {
530
			return array('error' => $this->error(self::ERROR_SESSION_EXPIRES));
531
		}
532
		
533
		if (!$this->commandExists($cmd)) {
534
			return array('error' => $this->error(self::ERROR_UNKNOWN_CMD));
535
		}
536
		
537
		if (!empty($args['mimes']) && is_array($args['mimes'])) {
538
			foreach ($this->volumes as $id => $v) {
539
				$this->volumes[$id]->setMimesFilter($args['mimes']);
540
			}
541
		}
542
543
		// call pre handlers for this command
544
		$args['sessionCloseEarlier'] = isset($this->sessionUseCmds[$cmd])? false : $this->sessionCloseEarlier;
545
		if (!empty($this->listeners[$cmd.'.pre'])) {
546
			$volume = isset($args['target'])? $this->volume($args['target']) : false;
547 View Code Duplication
			foreach ($this->listeners[$cmd.'.pre'] as $handler) {
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...
548
				call_user_func_array($handler, array($cmd, &$args, $this, $volume));
549
			}
550
		}
551
		
552
		// unlock session data for multiple access
553
		$this->sessionCloseEarlier && $args['sessionCloseEarlier'] && session_id() && session_write_close();
554
		
555
		if (substr(PHP_OS,0,3) === 'WIN') {
556
			// set time out
557
			if (($_max_execution_time = ini_get('max_execution_time')) && $_max_execution_time < 300) {
558
				@set_time_limit(300);
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...
559
			}
560
		}
561
		
562
		$result = $this->$cmd($args);
563
		
564
		if (isset($result['removed'])) {
565
			foreach ($this->volumes as $volume) {
566
				$result['removed'] = array_merge($result['removed'], $volume->removed());
567
				$volume->resetRemoved();
568
			}
569
		}
570
		
571
		// call handlers for this command
572
		if (!empty($this->listeners[$cmd])) {
573
			foreach ($this->listeners[$cmd] as $handler) {
574
				if (call_user_func_array($handler,array($cmd,&$result,$args,$this))) {
575
					// handler return true to force sync client after command completed
576
					$result['sync'] = true;
577
				}
578
			}
579
		}
580
		
581
		// replace removed files info with removed files hashes
582
		if (!empty($result['removed'])) {
583
			$removed = array();
584
			foreach ($result['removed'] as $file) {
585
				$removed[] = $file['hash'];
586
			}
587
			$result['removed'] = array_unique($removed);
588
		}
589
		// remove hidden files and filter files by mimetypes
590
		if (!empty($result['added'])) {
591
			$result['added'] = $this->filter($result['added']);
592
		}
593
		// remove hidden files and filter files by mimetypes
594
		if (!empty($result['changed'])) {
595
			$result['changed'] = $this->filter($result['changed']);
596
		}
597
		
598
		if ($this->debug || !empty($args['debug'])) {
599
			$result['debug'] = array(
600
				'connector' => 'php', 
601
				'phpver'    => PHP_VERSION,
602
				'time'      => $this->utime() - $this->time,
603
				'memory'    => (function_exists('memory_get_peak_usage') ? ceil(memory_get_peak_usage()/1024).'Kb / ' : '').ceil(memory_get_usage()/1024).'Kb / '.ini_get('memory_limit'),
604
				'upload'    => $this->uploadDebug,
605
				'volumes'   => array(),
606
				'mountErrors' => $this->mountErrors
607
				);
608
			
609
			foreach ($this->volumes as $id => $volume) {
610
				$result['debug']['volumes'][] = $volume->debug();
611
			}
612
		}
613
		
614
		foreach ($this->volumes as $volume) {
615
			$volume->umount();
616
		}
617
		
618
		if (!empty($result['callback'])) {
619
			$result['callback']['json'] = json_encode($result);
620
			$this->callback($result['callback']);
621
		} else {
622
			return $result;
623
		}
624
	}
625
	
626
	/**
627
	 * Return file real path
628
	 *
629
	 * @param  string  $hash  file hash
630
	 * @return string
631
	 * @author Dmitry (dio) Levashov
632
	 **/
633
	public function realpath($hash)	{
634
		if (($volume = $this->volume($hash)) == false) {
635
			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 elFinder::realpath of type string.

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...
636
		}
637
		return $volume->realpath($hash);
638
	}
639
	
640
	/**
641
	 * Return network volumes config.
642
	 *
643
	 * @return array
644
	 * @author Dmitry (dio) Levashov
645
	 */
646
	protected function getNetVolumes() {
0 ignored issues
show
Coding Style introduced by
getNetVolumes 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...
647
		if (isset($_SESSION[$this->netVolumesSessionKey])) {
648
			if ($data = elFinder::sessionDataDecode($_SESSION[$this->netVolumesSessionKey], '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...
649
				return $data;
650
			}
651
		}
652
		return array();
653
	}
654
655
	/**
656
	 * Save network volumes config.
657
	 *
658
	 * @param  array  $volumes  volumes config
659
	 * @return void
660
	 * @author Dmitry (dio) Levashov
661
	 */
662
	protected function saveNetVolumes($volumes) {
0 ignored issues
show
Coding Style introduced by
saveNetVolumes 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...
663
		$_SESSION[$this->netVolumesSessionKey] = elFinder::sessionDataEncode($volumes);
664
	}
665
666
	/**
667
	 * Remove netmount volume
668
	 * 
669
	 * @param string $key  netvolume key
670
	 */
671
	protected function removeNetVolume($key) {
672
		$netVolumes = $this->getNetVolumes();
673
		if (is_string($key) && isset($netVolumes[$key])) {
674
			unset($netVolumes[$key]);
675
			$this->saveNetVolumes($netVolumes);
676
		}
677
	}
678
679
	/**
680
	 * Get plugin instance & set to $this->plugins
681
	 * 
682
	 * @param  string $name   Plugin name (dirctory name)
683
	 * @param  array  $opts   Plugin options (optional)
684
	 * @return object | bool Plugin object instance Or false
685
	 * @author Naoki Sawada
686
	 */
687
	protected function getPluginInstance($name, $opts = array()) {
688
		$key = strtolower($name);
689
		if (! isset($this->plugins[$key])) {
690
			$p_file = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . $name . DIRECTORY_SEPARATOR . 'plugin.php';
691
			if (is_file($p_file)) {
692
				require_once $p_file;
693
				$class = 'elFinderPlugin' . $name;
694
				$this->plugins[$key] = new $class($opts);
695
			} else {
696
				$this->plugins[$key] = false;
697
			}
698
		}
699
		return $this->plugins[$key];
700
	}
701
702
	/***************************************************************************/
703
	/*                                 commands                                */
704
	/***************************************************************************/
705
	
706
	/**
707
	 * Normalize error messages
708
	 *
709
	 * @return array
710
	 * @author Dmitry (dio) Levashov
711
	 **/
712
	public function error() {
713
		$errors = array();
714
715
		foreach (func_get_args() as $msg) {
716
			if (is_array($msg)) {
717
				$errors = array_merge($errors, $msg);
718
			} else {
719
				$errors[] = $msg;
720
			}
721
		}
722
		
723
		return count($errors) ? $errors : array(self::ERROR_UNKNOWN);
724
	}
725
	
726
	protected function netmount($args) {
727
		// try session restart
728
		try {
729
			session_start();
730
		} catch (Exception $e) {}
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
731
		
732
		$options  = array();
733
		$protocol = $args['protocol'];
734
		
735
		if ($protocol === 'netunmount') {
736
			$key = $args['host'];
737
			$netVolumes = $this->getNetVolumes();
738
			if ($netVolumes[$key]) {
739
				$res = true;
740
				$volume = $this->volume($args['user']);
741
				if (method_exists($volume, 'netunmount')) {
742
					$res = $volume->netunmount($netVolumes, $key);
743
				}
744
				if ($res) {
745
					unset($netVolumes[$key]);
746
					$this->saveNetVolumes($netVolumes);
747
					return array('sync' => true);
748
				}
749
			}
750
			return array('error' => $this->error(self::ERROR_NETUNMOUNT));
751
		}
752
		
753
		$driver   = isset(self::$netDrivers[$protocol]) ? self::$netDrivers[$protocol] : '';
754
		$class    = 'elfindervolume'.$driver;
755
756
		if (!class_exists($class)) {
757
			return array('error' => $this->error(self::ERROR_NETMOUNT, $args['host'], self::ERROR_NETMOUNT_NO_DRIVER));
758
		}
759
760
		if (!$args['path']) {
761
			$args['path'] = '/';
762
		}
763
764
		foreach ($args as $k => $v) {
765
			if ($k != 'options' && $k != 'protocol' && $v) {
766
				$options[$k] = $v;
767
			}
768
		}
769
770
		if (is_array($args['options'])) {
771
			foreach ($args['options'] as $key => $value) {
772
				$options[$key] = $value;
773
			}
774
		}
775
776
		$volume = new $class();
777
		
778
		if (method_exists($volume, 'netmountPrepare')) {
779
			$options = $volume->netmountPrepare($options);
780
			if (isset($options['exit'])) {
781
				if ($options['exit'] === 'callback') {
782
					$this->callback($options['out']);
783
				}
784
				return $options;
785
			}
786
		}
787
		
788
		$netVolumes = $this->getNetVolumes();
789
		if ($volume->mount($options)) {
790
			if (! $key = @ $volume->netMountKey) {
791
				$key = md5($protocol . '-' . join('-', $options));
792
			}
793
			$options['driver'] = $driver;
794
			$options['netkey'] = $key;
795
			$netVolumes[$key]  = $options;
796
			$this->saveNetVolumes($netVolumes);
797
			$rootstat = $volume->file($volume->root());
798
			$rootstat['netkey'] = $key;
799
			return array('added' => array($rootstat));
800
		} else {
801
			$this->removeNetVolume($volume);
0 ignored issues
show
Documentation introduced by
$volume is of type object, 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...
802
			return array('error' => $this->error(self::ERROR_NETMOUNT, $args['host'], implode(' ', $volume->error())));
803
		}
804
805
	}
806
807
	/**
808
	 * "Open" directory
809
	 * Return array with following elements
810
	 *  - cwd          - opened dir info
811
	 *  - files        - opened dir content [and dirs tree if $args[tree]]
812
	 *  - api          - api version (if $args[init])
813
	 *  - uplMaxSize   - if $args[init]
814
	 *  - error        - on failed
815
	 *
816
	 * @param  array  command arguments
817
	 * @return array
818
	 * @author Dmitry (dio) Levashov
819
	 **/
820
	protected function open($args) {
821
		$target = $args['target'];
822
		$init   = !empty($args['init']);
823
		$tree   = !empty($args['tree']);
824
		$volume = $this->volume($target);
825
		$cwd    = $volume ? $volume->dir($target) : false;
826
		$hash   = $init ? 'default folder' : '#'.$target;
827
		$sleep  = 0;
0 ignored issues
show
Unused Code introduced by
$sleep 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...
828
		$compare = '';
829
830
		// on init request we can get invalid dir hash -
831
		// dir which can not be opened now, but remembered by client,
832
		// so open default dir
833
		if ((!$cwd || !$cwd['read']) && $init) {
834
			$volume = $this->default;
835
			$cwd    = $volume->dir($volume->defaultPath());
836
		}
837
		
838
		if (!$cwd) {
839
			return array('error' => $this->error(self::ERROR_OPEN, $hash, self::ERROR_DIR_NOT_FOUND));
840
		}
841
		if (!$cwd['read']) {
842
			return array('error' => $this->error(self::ERROR_OPEN, $hash, self::ERROR_PERM_DENIED));
843
		}
844
845
		$files = array();
846
847
		// get other volume root
848
		if ($tree) {
849
			foreach ($this->volumes as $id => $v) {
850
				$files[] = $v->file($v->root());
851
			}
852
		}
853
854
		// get current working directory files list and add to $files if not exists in it
855 View Code Duplication
		if (($ls = $volume->scandir($cwd['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...
856
			return array('error' => $this->error(self::ERROR_OPEN, $cwd['name'], $volume->error()));
857
		}
858
		// long polling mode
859
		if ($args['compare']) {
860
			$sleep = max(1, (int)$volume->getOption('lsPlSleep'));
861
			$limit = max(1, (int)$volume->getOption('plStandby') / $sleep) + 1;
862
			$timelimit = ini_get('max_execution_time');
863
			$compare = $args['compare'];
0 ignored issues
show
Unused Code introduced by
$compare 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...
864
			do {
865
				$timelimit && @ set_time_limit($timelimit + $sleep);
866
				$_mtime = 0;
867
				foreach($ls as $_f) {
868
					$_mtime = max($_mtime, $_f['ts']);
869
				}
870
				$compare = strval(count($ls)).':'.strval($_mtime);
871
				if ($compare !== $args['compare']) {
872
					break;
873
				}
874
				if (--$limit) {
875
					sleep($sleep);
876
					$volume->clearstatcache();
877
					if (($ls = $volume->scandir($cwd['hash'])) === false) {
878
						break;
879
					}
880
				}
881
			} while($limit);
882 View Code Duplication
			if ($ls === 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...
883
				return array('error' => $this->error(self::ERROR_OPEN, $cwd['name'], $volume->error()));
884
			}
885
		}
886
		
887
		if ($ls) {
888
			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...
889
				$files = array_merge($files, $ls);
890
				$files = array_unique($files, SORT_REGULAR);
891
			} else {
892
				$files = $ls;
893
			}
894
		}
895
		
896
		$result = array(
897
			'cwd'     => $cwd,
898
			'options' => $volume->options($cwd['hash']),
899
			'files'   => $files
900
		);
901
		
902
		if ($compare) {
903
			$result['cwd']['compare'] = $compare;
904
		}
905
		
906
		if (!empty($args['init'])) {
907
			$result['api'] = $this->version;
908
			$result['uplMaxSize'] = ini_get('upload_max_filesize');
909
			$result['uplMaxFile'] = ini_get('max_file_uploads');
910
			$result['netDrivers'] = array_keys(self::$netDrivers);
911
			if ($volume) {
912
				$result['cwd']['root'] = $volume->root();
913
			}
914
		}
915
		
916
		return $result;
917
	}
918
	
919
	/**
920
	 * Return dir files names list
921
	 *
922
	 * @param  array  command arguments
923
	 * @return array
924
	 * @author Dmitry (dio) Levashov
925
	 **/
926 View Code Duplication
	protected function ls($args) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
927
		$target = $args['target'];
928
		
929
		if (($volume = $this->volume($target)) == false
930
		|| ($list = $volume->ls($target)) === false) {
931
			return array('error' => $this->error(self::ERROR_OPEN, '#'.$target));
932
		}
933
		return array('list' => $list);
934
	}
935
	
936
	/**
937
	 * Return subdirs for required directory
938
	 *
939
	 * @param  array  command arguments
940
	 * @return array
941
	 * @author Dmitry (dio) Levashov
942
	 **/
943 View Code Duplication
	protected function tree($args) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
944
		$target = $args['target'];
945
		
946
		if (($volume = $this->volume($target)) == false
947
		|| ($tree = $volume->tree($target)) == false) {
948
			return array('error' => $this->error(self::ERROR_OPEN, '#'.$target));
949
		}
950
951
		return array('tree' => $tree);
952
	}
953
	
954
	/**
955
	 * Return parents dir for required directory
956
	 *
957
	 * @param  array  command arguments
958
	 * @return array
959
	 * @author Dmitry (dio) Levashov
960
	 **/
961 View Code Duplication
	protected function parents($args) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
962
		$target = $args['target'];
963
		
964
		if (($volume = $this->volume($target)) == false
965
		|| ($tree = $volume->parents($target)) == false) {
966
			return array('error' => $this->error(self::ERROR_OPEN, '#'.$target));
967
		}
968
969
		return array('tree' => $tree);
970
	}
971
	
972
	/**
973
	 * Return new created thumbnails list
974
	 *
975
	 * @param  array  command arguments
976
	 * @return array
977
	 * @author Dmitry (dio) Levashov
978
	 **/
979
	protected function tmb($args) {
980
		
981
		$result  = array('images' => array());
982
		$targets = $args['targets'];
983
		
984
		foreach ($targets as $target) {
985
			if (($volume = $this->volume($target)) != false
986
			&& (($tmb = $volume->tmb($target)) != false)) {
987
				$result['images'][$target] = $tmb;
988
			}
989
		}
990
		return $result;
991
	}
992
	
993
	/**
994
	 * Required to output file in browser when volume URL is not set 
995
	 * Return array contains opened file pointer, root itself and required headers
996
	 *
997
	 * @param  array  command arguments
998
	 * @return array
999
	 * @author Dmitry (dio) Levashov
1000
	 **/
1001
	protected function file($args) {
0 ignored issues
show
Coding Style introduced by
file 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...
1002
		$target   = $args['target'];
1003
		$download = !empty($args['download']);
1004
		$h403     = 'HTTP/1.x 403 Access Denied';
1005
		$h404     = 'HTTP/1.x 404 Not Found';
1006
1007 View Code Duplication
		if (($volume = $this->volume($target)) == 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...
1008
			return array('error' => 'File not found', 'header' => $h404, 'raw' => true);
1009
		}
1010
		
1011 View Code Duplication
		if (($file = $volume->file($target)) == 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...
1012
			return array('error' => 'File not found', 'header' => $h404, 'raw' => true);
1013
		}
1014
		
1015
		if (!$file['read']) {
1016
			return array('error' => 'Access denied', 'header' => $h403, 'raw' => true);
1017
		}
1018
		
1019 View Code Duplication
		if (($fp = $volume->open($target)) == 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...
1020
			return array('error' => 'File not found', 'header' => $h404, 'raw' => true);
1021
		}
1022
1023
		// allow change MIME type by 'file.pre' callback functions
1024
		$mime = isset($args['mime'])? $args['mime'] : $file['mime'];
1025
		if ($download) {
1026
			$disp = 'attachment';
1027
		} else {
1028
			$dispInlineRegex = $volume->getOption('dispInlineRegex');
1029
			$inlineRegex = false;
1030
			if ($dispInlineRegex) {
1031
				$inlineRegex = '#' . str_replace('#', '\\#', $dispInlineRegex) . '#';
1032
				try {
1033
					preg_match($inlineRegex, '');
1034
				} catch(Exception $e) {
1035
					$inlineRegex = false;
1036
				}
1037
			}
1038
			if (!$inlineRegex) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $inlineRegex 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...
1039
				$inlineRegex = '#^(?:(?:image|text)|application/x-shockwave-flash$)#';
1040
			}
1041
			$disp  = preg_match($inlineRegex, $mime)? 'inline' : 'attachment';
1042
		}
1043
		
1044
		$filenameEncoded = rawurlencode($file['name']);
1045
		if (strpos($filenameEncoded, '%') === false) { // ASCII only
1046
			$filename = 'filename="'.$file['name'].'"';
1047
		} else {
1048
			$ua = $_SERVER['HTTP_USER_AGENT'];
1049
			if (preg_match('/MSIE [4-8]/', $ua)) { // IE < 9 do not support RFC 6266 (RFC 2231/RFC 5987)
1050
				$filename = 'filename="'.$filenameEncoded.'"';
1051
			} elseif (strpos($ua, 'Chrome') === false && strpos($ua, 'Safari') !== false && preg_match('#Version/[3-5]#', $ua)) { // Safari < 6
1052
				$filename = 'filename="'.str_replace('"', '', $file['name']).'"';
1053
			} else { // RFC 6266 (RFC 2231/RFC 5987)
0 ignored issues
show
Unused Code Comprehensibility introduced by
36% 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...
1054
				$filename = 'filename*=UTF-8\'\''.$filenameEncoded;
1055
			}
1056
		}
1057
		
1058
		$result = array(
1059
			'volume'  => $volume,
1060
			'pointer' => $fp,
1061
			'info'    => $file,
1062
			'header'  => array(
1063
				'Content-Type: '.$mime, 
1064
				'Content-Disposition: '.$disp.'; '.$filename,
1065
				'Content-Transfer-Encoding: binary',
1066
				'Content-Length: '.$file['size'],
1067
				'Connection: close'
1068
			)
1069
		);
1070
		if (isset($file['url']) && $file['url'] && $file['url'] != 1) {
1071
			$result['header'][] = 'Content-Location: '.$file['url'];
1072
		}
1073
		return $result;
1074
	}
1075
	
1076
	/**
1077
	 * Count total files size
1078
	 *
1079
	 * @param  array  command arguments
1080
	 * @return array
1081
	 * @author Dmitry (dio) Levashov
1082
	 **/
1083
	protected function size($args) {
1084
		$size = 0;
1085
		
1086
		foreach ($args['targets'] as $target) {
1087
			if (($volume = $this->volume($target)) == false
1088
			|| ($file = $volume->file($target)) == false
1089
			|| !$file['read']) {
1090
				return array('error' => $this->error(self::ERROR_OPEN, '#'.$target));
1091
			}
1092
			
1093
			$size += $volume->size($target);
1094
		}
1095
		return array('size' => $size);
1096
	}
1097
	
1098
	/**
1099
	 * Create directory
1100
	 *
1101
	 * @param  array  command arguments
1102
	 * @return array
1103
	 * @author Dmitry (dio) Levashov
1104
	 **/
1105 View Code Duplication
	protected function mkdir($args) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
1106
		$target = $args['target'];
1107
		$name   = $args['name'];
1108
		
1109
		if (($volume = $this->volume($target)) == false) {
1110
			return array('error' => $this->error(self::ERROR_MKDIR, $name, self::ERROR_TRGDIR_NOT_FOUND, '#'.$target));
1111
		}
1112
1113
		return ($dir = $volume->mkdir($target, $name)) == false
1114
			? array('error' => $this->error(self::ERROR_MKDIR, $name, $volume->error()))
1115
			: array('added' => array($dir));
1116
	}
1117
	
1118
	/**
1119
	 * Create empty file
1120
	 *
1121
	 * @param  array  command arguments
1122
	 * @return array
1123
	 * @author Dmitry (dio) Levashov
1124
	 **/
1125 View Code Duplication
	protected function mkfile($args) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
1126
		$target = $args['target'];
1127
		$name   = $args['name'];
1128
		
1129
		if (($volume = $this->volume($target)) == false) {
1130
			return array('error' => $this->error(self::ERROR_MKFILE, $name, self::ERROR_TRGDIR_NOT_FOUND, '#'.$target));
1131
		}
1132
1133
		return ($file = $volume->mkfile($target, $args['name'])) == false
1134
			? array('error' => $this->error(self::ERROR_MKFILE, $name, $volume->error()))
1135
			: array('added' => array($file));
1136
	}
1137
	
1138
	/**
1139
	 * Rename file
1140
	 *
1141
	 * @param  array  $args
1142
	 * @return array
1143
	 * @author Dmitry (dio) Levashov
1144
	 **/
1145
	protected function rename($args) {
1146
		$target = $args['target'];
1147
		$name   = $args['name'];
1148
		
1149 View Code Duplication
		if (($volume = $this->volume($target)) == 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...
1150
		||  ($rm  = $volume->file($target)) == false) {
1151
			return array('error' => $this->error(self::ERROR_RENAME, '#'.$target, self::ERROR_FILE_NOT_FOUND));
1152
		}
1153
		$rm['realpath'] = $volume->realpath($target);
1154
		
1155
		return ($file = $volume->rename($target, $name)) == false
1156
			? array('error' => $this->error(self::ERROR_RENAME, $rm['name'], $volume->error()))
1157
			: array('added' => array($file), 'removed' => array($rm));
1158
	}
1159
	
1160
	/**
1161
	 * Duplicate file - create copy with "copy %d" suffix
1162
	 *
1163
	 * @param array  $args  command arguments
1164
	 * @return array
1165
	 * @author Dmitry (dio) Levashov
1166
	 **/
1167
	protected function duplicate($args) {
1168
		$targets = is_array($args['targets']) ? $args['targets'] : array();
1169
		$result  = array('added' => array());
1170
		$suffix  = empty($args['suffix']) ? 'copy' : $args['suffix'];
1171
		
1172
		foreach ($targets as $target) {
1173 View Code Duplication
			if (($volume = $this->volume($target)) == 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...
1174
			|| ($src = $volume->file($target)) == false) {
1175
				$result['warning'] = $this->error(self::ERROR_COPY, '#'.$target, self::ERROR_FILE_NOT_FOUND);
1176
				break;
1177
			}
1178
			
1179
			if (($file = $volume->duplicate($target, $suffix)) == false) {
1180
				$result['warning'] = $this->error($volume->error());
1181
				break;
1182
			}
1183
			
1184
			$result['added'][] = $file;
1185
		}
1186
		
1187
		return $result;
1188
	}
1189
		
1190
	/**
1191
	 * Remove dirs/files
1192
	 *
1193
	 * @param array  command arguments
1194
	 * @return array
1195
	 * @author Dmitry (dio) Levashov
1196
	 **/
1197
	protected function rm($args) {
1198
		$targets = is_array($args['targets']) ? $args['targets'] : array();
1199
		$result  = array('removed' => array());
1200
		
1201
		foreach ($targets as $target) {
1202 View Code Duplication
			if (($volume = $this->volume($target)) == 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...
1203
				$result['warning'] = $this->error(self::ERROR_RM, '#'.$target, self::ERROR_FILE_NOT_FOUND);
1204
				return $result;
1205
			}
1206
			if (!$volume->rm($target)) {
1207
				$result['warning'] = $this->error($volume->error());
1208
				return $result;
1209
			}
1210
		}
1211
1212
		return $result;
1213
	}
1214
1215
	/**
1216
	* Get remote contents
1217
	*
1218
	* @param  string   $url     target url
1219
	* @param  int      $timeout timeout (sec)
1220
	* @param  int      $redirect_max redirect max count
1221
	* @param  string   $ua
1222
	* @param  resource $fp
1223
	* @return string or bool(false)
1224
	* @retval string contents
1225
	* @retval false  error
1226
	* @author Naoki Sawada
1227
	**/
1228
	protected function get_remote_contents( &$url, $timeout = 30, $redirect_max = 5, $ua = 'Mozilla/5.0', $fp = null ) {
1229
		$method = (function_exists('curl_exec') && !ini_get('safe_mode'))? 'curl_get_contents' : 'fsock_get_contents'; 
1230
		return $this->$method( $url, $timeout, $redirect_max, $ua, $fp );
1231
	}
1232
	
1233
	/**
1234
	 * Get remote contents with cURL
1235
	 *
1236
	 * @param  string   $url     target url
1237
	 * @param  int      $timeout timeout (sec)
1238
	 * @param  int      $redirect_max redirect max count
1239
	 * @param  string   $ua
1240
	 * @param  resource $outfp
1241
	 * @return string or bool(false)
1242
	 * @retval string contents
1243
	 * @retval false  error
1244
	 * @author Naoki Sawada
1245
	 **/
1246
	 protected function curl_get_contents( &$url, $timeout, $redirect_max, $ua, $outfp ){
1247
		$ch = curl_init();
1248
		curl_setopt( $ch, CURLOPT_URL, $url );
1249
		curl_setopt( $ch, CURLOPT_HEADER, false );
1250
		if ($outfp) {
1251
			curl_setopt( $ch, CURLOPT_FILE, $outfp );
1252
		} else {
1253
			curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
1254
			curl_setopt( $ch, CURLOPT_BINARYTRANSFER, true );
1255
		}
1256
		curl_setopt( $ch, CURLOPT_LOW_SPEED_LIMIT, 1 );
1257
		curl_setopt( $ch, CURLOPT_LOW_SPEED_TIME, $timeout );
1258
		curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false );
1259
		curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, 1);
1260
		curl_setopt( $ch, CURLOPT_MAXREDIRS, $redirect_max);
1261
		curl_setopt( $ch, CURLOPT_USERAGENT, $ua);
1262
		$result = curl_exec( $ch );
1263
		$url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
1264
		curl_close( $ch );
1265
		return $outfp? $outfp : $result;
1266
	}
1267
	
1268
	/**
1269
	 * Get remote contents with fsockopen()
1270
	 *
1271
	 * @param  string   $url          url
1272
	 * @param  int      $timeout      timeout (sec)
1273
	 * @param  int      $redirect_max redirect max count
1274
	 * @param  string   $ua
1275
	 * @param  resource $outfp
1276
	 * @return string or bool(false)
1277
	 * @retval string contents
1278
	 * @retval false  error
1279
	 * @author Naoki Sawada
1280
	 */
1281
	protected function fsock_get_contents( &$url, $timeout, $redirect_max, $ua, $outfp ) {
1282
1283
		$connect_timeout = 3;
1284
		$connect_try = 3;
1285
		$method = 'GET';
1286
		$readsize = 4096;
1287
1288
		$getSize = null;
1289
		$headers = '';
1290
		
1291
		$arr = parse_url($url);
1292
		if (!$arr){
1293
			// Bad request
1294
			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 elFinder::fsock_get_contents of type string.

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...
1295
		}
1296
		
1297
		// query
1298
		$arr['query'] = isset($arr['query']) ? '?'.$arr['query'] : '';
1299
		// port
1300
		$arr['port'] = isset($arr['port']) ? $arr['port'] : (!empty($arr['https'])? 443 : 80);
1301
		
1302
		$url_base = $arr['scheme'].'://'.$arr['host'].':'.$arr['port'];
1303
		$url_path = isset($arr['path']) ? $arr['path'] : '/';
1304
		$uri = $url_path.$arr['query'];
1305
		
1306
		$query = $method.' '.$uri." HTTP/1.0\r\n";
1307
		$query .= "Host: ".$arr['host']."\r\n";
1308
		if (!empty($ua)) $query .= "User-Agent: ".$ua."\r\n";
1309
		if (!is_null($getSize)) $query .= 'Range: bytes=0-' . ($getSize - 1) . "\r\n";
1310
		
1311
		$query .= $headers;
1312
1313
		$query .= "\r\n";
1314
1315
		$fp = $connect_try_count = 0;
1316
		while( !$fp && $connect_try_count < $connect_try ) {
1317
	
1318
			$errno = 0;
1319
			$errstr = "";
1320
			$fp = @ fsockopen(
1321
			$arr['https'].$arr['host'],
1322
			$arr['port'],
1323
			$errno,$errstr,$connect_timeout);
1324
			if ($fp) break;
1325
			$connect_try_count++;
1326
			if (connection_aborted()) {
1327
				exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The method fsock_get_contents() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1328
			}
1329
			sleep(1); // wait 1sec
1330
		}
1331
		
1332
		$fwrite = 0;
0 ignored issues
show
Unused Code introduced by
$fwrite 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...
1333
		for ($written = 0; $written < strlen($query); $written += $fwrite) {
1334
			$fwrite = fwrite($fp, substr($query, $written));
1335
			if (!$fwrite) {
1336
				break;
1337
			}
1338
		}
1339
		
1340
		$response = '';
0 ignored issues
show
Unused Code introduced by
$response 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...
1341
		
1342
		if ($timeout) {
1343
			socket_set_timeout($fp, $timeout);
1344
		}
1345
		
1346
		$_response = '';
1347
		$header = '';
1348
		while($_response !== "\r\n"){
1349
			$_response = fgets($fp, $readsize);
1350
			$header .= $_response;
1351
		};
1352
		
1353
		$rccd = array_pad(explode(' ',$header,3), 3, ''); // array('HTTP/1.1','200','OK\r\n...')
0 ignored issues
show
Unused Code Comprehensibility introduced by
89% 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...
1354
		$rc = (int)$rccd[1];
1355
		
1356
		// Redirect
1357
		switch ($rc) {
1358
			case 307: // Temporary Redirect
1359
			case 303: // See Other
1360
			case 302: // Moved Temporarily
1361
			case 301: // Moved Permanently
1362
				$matches = array();
1363
				if (preg_match('/^Location: (.+?)(#.+)?$/im',$header,$matches) && --$redirect_max > 0) {
1364
					$url = trim($matches[1]);
1365
					$hash = isset($matches[2])? trim($matches[2]) : '';
0 ignored issues
show
Unused Code introduced by
$hash 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...
1366
					if (!preg_match('/^https?:\//',$url)) { // no scheme
1367
						if ($url{0} != '/') { // Relative path
1368
							// to Absolute path
1369
							$url = substr($url_path,0,strrpos($url_path,'/')).'/'.$url;
1370
						}
1371
						// add sheme,host
1372
						$url = $url_base.$url;
1373
					}
1374
					fclose($fp);
1375
					return $this->fsock_get_contents( $url, $timeout, $redirect_max, $ua, $outfp );
1376
				}
1377
		}
1378
		
1379
		$body = '';
1380
		if (!$outfp) {
1381
			$outfp = fopen('php://temp', 'rwb');
1382
			$body = true;
1383
		}
1384
		while(fwrite($outfp, fread($fp, $readsize))) {
1385
			if ($timeout) {
1386
				$_status = socket_get_status($fp);
1387
				if ($_status['timed_out']) {
1388
					fclose($outfp);
1389
					fclose($fp);
1390
					return false; // Request Time-out
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by elFinder::fsock_get_contents of type string.

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...
1391
				}
1392
			}
1393
		}
1394
		if ($body) {
1395
			rewind($outfp);
1396
			$body = stream_get_contents($outfp);
1397
			fclose($outfp);
1398
			$outfp = null;
1399
		}
1400
		
1401
		fclose($fp);
1402
		
1403
		return $outfp? $outfp : $body; // Data
0 ignored issues
show
Bug Compatibility introduced by
The expression $outfp ? $outfp : $body; of type boolean|string adds the type boolean to the return on line 1403 which is incompatible with the return type documented by elFinder::fsock_get_contents of type string.
Loading history...
1404
	}
1405
	
1406
	/**
1407
	 * Parse Data URI scheme
1408
	 * 
1409
	 * @param  string $str
1410
	 * @param  array  $extTable
1411
	 * @return array
1412
	 * @author Naoki Sawada
1413
	 */
1414
	protected function parse_data_scheme( $str, $extTable ) {
1415
		$data = $name = '';
1416
		if ($fp = fopen('data://'.substr($str, 5), 'rb')) {
1417 View Code Duplication
			if ($data = stream_get_contents($fp)) {
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...
1418
				$meta = stream_get_meta_data($fp);
1419
				$ext = isset($extTable[$meta['mediatype']])? '.' . $extTable[$meta['mediatype']] : '';
1420
				$name = substr(md5($data), 0, 8) . $ext;
1421
			}
1422
			fclose($fp);
1423
		}
1424
		return array($data, $name);
1425
	}
1426
	
1427
	/**
1428
	 * Detect file type extension by local path
1429
	 * 
1430
	 * @param  string $path Local path
1431
	 * @return string file type extension with dot
1432
	 * @author Naoki Sawada
1433
	 */
1434
	protected function detectFileExtension($path) {
1435
		static $type, $finfo, $extTable;
1436
		if (!$type) {
1437
			$keys = array_keys($this->volumes);
1438
			$volume = $this->volumes[$keys[0]];
1439
			$extTable = array_flip(array_unique($volume->getMimeTable()));
1440
			
1441
			if (class_exists('finfo', false)) {
1442
				$tmpFileInfo = @explode(';', @finfo_file(finfo_open(FILEINFO_MIME), __FILE__));
1443
			} else {
1444
				$tmpFileInfo = false;
1445
			}
1446
			$regexp = '/text\/x\-(php|c\+\+)/';
1447
			if ($tmpFileInfo && preg_match($regexp, array_shift($tmpFileInfo))) {
1448
				$type = 'finfo';
1449
				$finfo = finfo_open(FILEINFO_MIME);
1450
			} elseif (function_exists('mime_content_type')
1451
					&& 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...
1452
				$type = 'mime_content_type';
1453
			} elseif (function_exists('getimagesize')) {
1454
				$type = 'getimagesize';
1455
			} else {
1456
				$type = 'none';
1457
			}
1458
		}
1459
		
1460
		$mime = '';
1461
		if ($type === 'finfo') {
1462
			$mime = @finfo_file($finfo, $path);
1463
		} elseif ($type === 'mime_content_type') {
1464
			$mime = mime_content_type($path);
1465
		} elseif ($type === 'getimagesize') {
1466
			if ($img = @getimagesize($path)) {
1467
				$mime = $img['mime'];
1468
			}
1469
		}
1470
		
1471
		if ($mime) {
1472
			$mime = explode(';', $mime);
1473
			$mime = trim($mime[0]);
1474
			
1475 View Code Duplication
			if (in_array($mime, 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...
1476
				// finfo return this mime for empty files
1477
				$mime = 'text/plain';
1478
			} elseif ($mime == 'application/x-zip') {
1479
				// http://elrte.org/redmine/issues/163
1480
				$mime = 'application/zip';
1481
			}
1482
		}
1483
		
1484
		return ($mime && isset($extTable[$mime]))? ('.' . $extTable[$mime]) : '';
1485
	}
1486
	
1487
	/**
1488
	 * Get temporary dirctroy path
1489
	 * 
1490
	 * @param  string $volumeTempPath
1491
	 * @return string
1492
	 * @author Naoki Sawada
1493
	 */
1494
	private function getTempDir($volumeTempPath = null) {
1495
		$testDirs = array();
1496
		if ($this->uploadTempPath) {
1497
			$testDirs[] = rtrim(realpath($this->uploadTempPath), DIRECTORY_SEPARATOR);
1498
		}
1499
		if ($volumeTempPath) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $volumeTempPath 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...
1500
			$testDirs[] = rtrim(realpath($volumeTempPath), DIRECTORY_SEPARATOR);
1501
		}
1502
		if (function_exists('sys_get_temp_dir')) {
1503
			$testDirs[] = sys_get_temp_dir();
1504
		}
1505
		$tempDir = '';
1506
		foreach($testDirs as $testDir) {
1507
			if (!$testDir || !is_dir($testDir)) continue;
1508
			if (is_writable($testDir)) {
1509
				$tempDir = $testDir;
1510
				$gc = time() - 3600;
1511
				foreach(glob($tempDir . DIRECTORY_SEPARATOR .'ELF*') as $cf) {
1512
					if (filemtime($cf) < $gc) {
1513
						@unlink($cf);
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...
1514
					}
1515
				}
1516
				break;
1517
			}
1518
		}
1519
		return $tempDir;
1520
	}
1521
	
1522
	/**
1523
	 * chmod
1524
	 *
1525
	 * @param array  command arguments
1526
	 * @return array
1527
	 * @author David Bartle
1528
	 **/
1529
	protected function chmod($args) {
1530
		$targets = $args['targets'];
1531
		$mode    = intval((string)$args['mode'], 8);
1532
1533
		if (!is_array($targets)) {
1534
			$targets = array($targets);
1535
		}
1536
		
1537
		$result = array();
1538
		
1539 View Code Duplication
		if (($volume = $this->volume($targets[0])) == 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...
1540
			$result['error'] = $this->error(self::ERROR_CONF_NO_VOL);
1541
			return $result;
1542
		}
1543
1544
		$files = array();
1545
		$errors = array();
1546
		foreach($targets as $target) {
1547
			$file = $volume->chmod($target, $mode);
1548
			if ($file) {
1549
				$files = array_merge($files, is_array($file)? $file : array($file));
1550
			} else {
1551
				$errors = array_merge($errors, $volume->error());
1552
			}
1553
		}
1554
		
1555
		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...
1556
			$result['changed'] = $files;
1557
			if ($errors) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $errors 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...
1558
				$result['warning'] = $this->error($errors);
1559
			}
1560
		} else {
1561
			$result['error'] = $this->error($errors);
1562
		}
1563
		
1564
		return $result;
1565
	}
1566
	
1567
	/**
1568
	 * Check chunked upload files
1569
	 * 
1570
	 * @param string $tmpname  uploaded temporary file path
1571
	 * @param string $chunk    uploaded chunk file name
1572
	 * @param string $cid      uploaded chunked file id
1573
	 * @param string $tempDir  temporary dirctroy path
1574
	 * @return array (string JoinedTemporaryFilePath, string FileName) or (empty, empty)
1575
	 * @author Naoki Sawada
1576
	 */
1577
	private function checkChunkedFile($tmpname, $chunk, $cid, $tempDir, $volume = null) {
0 ignored issues
show
Coding Style introduced by
checkChunkedFile 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...
1578
		if (preg_match('/^(.+)(\.\d+_(\d+))\.part$/s', $chunk, $m)) {
1579
			$fname = $m[1];
1580
			$encname = md5($cid . '_' . $fname);
1581
			$base = $tempDir . DIRECTORY_SEPARATOR . 'ELF' . $encname;
1582
			$clast = intval($m[3]);
1583
			if (is_null($tmpname)) {
1584
				ignore_user_abort(true);
1585
				sleep(10); // wait 10 sec
1586
				// chunked file upload fail
1587
				foreach(glob($base . '*') as $cf) {
1588
					@unlink($cf);
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...
1589
				}
1590
				ignore_user_abort(false);
1591
				return;
1592
			}
1593
			
1594
			$range = isset($_POST['range'])? trim($_POST['range']) : '';
1595
			if ($range && preg_match('/^(\d+),(\d+),(\d+)$/', $range, $ranges)) {
1596
				$start = $ranges[1];
1597
				$len   = $ranges[2];
1598
				$size  = $ranges[3];
1599
				$tmp = $base . '.part';
1600
				$csize = filesize($tmpname);
1601
				
1602
				$tmpExists = is_file($tmp);
1603
				if (!$tmpExists) {
1604
					// check upload max size
1605
					$uploadMaxSize = $volume->getUploadMaxSize();
1606
					if ($uploadMaxSize > 0 && $size > $uploadMaxSize) {
1607
						return array(self::ERROR_UPLOAD_FILE_SIZE, false);
1608
					}
1609
					// make temp file
1610
					$ok = false;
1611
					if ($fp = fopen($tmp, 'wb')) {
1612
						flock($fp, LOCK_EX);
1613
						$ok = ftruncate($fp, $size);
1614
						flock($fp, LOCK_UN);
1615
						fclose($fp);
1616
						touch($base);
1617
					}
1618
					if (!$ok) {
1619
						unlink($tmp);
1620
						return array(self::ERROR_UPLOAD_TEMP, false);
1621
					}
1622
				} else {
1623
					// wait until makeing temp file (for anothor session)
1624
					$cnt = 1200; // Time limit 120 sec
1625
					while(!is_file($base) && --$cnt) {
1626
						usleep(100000); // wait 100ms
1627
					}
1628
					if (!$cnt) {
1629
						return array(self::ERROR_UPLOAD_TEMP, false);
1630
					}
1631
				}
1632
				
1633
				// check size info
1634
				if ($len != $csize || $start + $len > $size || ($tmpExists && $size != filesize($tmp))) {
1635
					return array(self::ERROR_UPLOAD_TEMP, false);
1636
				}
1637
				
1638
				// write chunk data
1639
				$writelen = 0;
0 ignored issues
show
Unused Code introduced by
$writelen 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...
1640
				$src = fopen($tmpname, 'rb');
1641
				$fp = fopen($tmp, 'cb');
1642
				fseek($fp, $start);
1643
				$writelen = stream_copy_to_stream($src, $fp, $len);
1644
				fclose($fp);
1645
				fclose($src);
1646
				if ($writelen != $len) {
1647
					return array(self::ERROR_UPLOAD_TEMP, false);
1648
				}
1649
				
1650
				// write counts
1651
				file_put_contents($base, "\0", FILE_APPEND | LOCK_EX);
1652
				
1653
				if (filesize($base) >= $clast + 1) {
1654
					// Completion
1655
					unlink($base);
1656
					return array($tmp, $fname);
1657
				}
1658
			} else {
1659
				// old way
1660
				$part = $base . $m[2];
1661
				if (move_uploaded_file($tmpname, $part)) {
1662
					@chmod($part, 0600);
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...
1663
					if ($clast < count(glob($base . '*'))) {
1664
						$parts = array();
1665
						for ($i = 0; $i <= $clast; $i++) {
1666
							$name = $base . '.' . $i . '_' . $clast;
1667
							if (is_readable($name)) {
1668
								$parts[] = $name;
1669
							} else {
1670
								$parts = null;
1671
								break;
1672
							}
1673
						}
1674
						if ($parts) {
1675
							if (!is_file($base)) {
1676
								touch($base);
1677
								if ($resfile = tempnam($tempDir, 'ELF')) {
1678
									$target = fopen($resfile, 'wb');
1679
									foreach($parts as $f) {
1680
										$fp = fopen($f, 'rb');
1681
										while (!feof($fp)) {
1682
											fwrite($target, fread($fp, 8192));
1683
										}
1684
										fclose($fp);
1685
										unlink($f);
1686
									}
1687
									fclose($target);
1688
									unlink($base);
1689
									return array($resfile, $fname);
1690
								}
1691
								unlink($base);
1692
							}
1693
						}
1694
					}
1695
				}
1696
			}
1697
		}
1698
		return array('', '');
1699
	}
1700
	
1701
	/**
1702
	 * Save uploaded files
1703
	 *
1704
	 * @param  array
1705
	 * @return array
1706
	 * @author Dmitry (dio) Levashov
1707
	 **/
1708
	protected function upload($args) {
0 ignored issues
show
Coding Style introduced by
upload uses the super-global variable $GLOBALS 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
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...
1709
		$ngReg  = '/[\/\\?*:|"<>]/';
1710
		$target = $args['target'];
1711
		$volume = $this->volume($target);
1712
		$files  = isset($args['FILES']['upload']) && is_array($args['FILES']['upload']) ? $args['FILES']['upload'] : array();
1713
		$header = empty($args['html']) ? array() : array('header' => 'Content-Type: text/html; charset=utf-8');
1714
		$result = array_merge(array('added' => array()), $header);
1715
		$paths  = $args['upload_path']? $args['upload_path'] : array();
1716
		$chunk  = $args['chunk']? $args['chunk'] : '';
1717
		$cid    = $args['cid']? (int)$args['cid'] : '';
1718
		
1719
		$renames= array();
1720
		$suffix = '~';
1721
		if ($args['renames'] && is_array($args['renames'])) {
1722
			$renames = array_flip($args['renames']);
1723 View Code Duplication
			if (is_string($args['suffix']) && ! preg_match($ngReg, $args['suffix'])) {
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...
1724
				$suffix = $args['suffix'];
1725
			}
1726
		}
1727
		
1728
		if (!$volume) {
1729
			return array_merge(array('error' => $this->error(self::ERROR_UPLOAD, self::ERROR_TRGDIR_NOT_FOUND, '#'.$target)), $header);
1730
		}
1731
		
1732
		// regist Shutdown function
1733
		$GLOBALS['elFinderTempFiles'] = array();
1734
// 		if (version_compare(PHP_VERSION, '5.3.0', '>=')) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
57% 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...
1735
// 			$shutdownfunc = function(){ // <- Parse error on PHP < 5.3 ;-(
1736
// 				foreach(array_keys($GLOBALS['elFinderTempFiles']) as $f){
1737
// 					@unlink($f);
1738
// 				}
1739
// 			};
1740
// 		} else {
1741
			$shutdownfunc = create_function('', '
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...
1742
				foreach(array_keys($GLOBALS[\'elFinderTempFiles\']) as $f){
1743
					@unlink($f);
1744
				}
1745
			');
1746
//		}
1747
		register_shutdown_function($shutdownfunc);
1748
		
1749
		// file extentions table by MIME
1750
		$extTable = array_flip(array_unique($volume->getMimeTable()));
1751
		
1752
		if (empty($files)) {
1753
			if (!$args['upload'] && $args['name'] && is_array($args['name'])) {
1754
				$error = '';
1755
				$result['name'] = array();
1756
				foreach($args['name'] as $_i => $_name) {
1757
					if (!$volume->isUploadableByName($_name)) {
1758
						$error = $this->error(self::ERROR_UPLOAD_FILE, $_name, self::ERROR_UPLOAD_FILE_MIME);
1759
						break;
1760
					}
1761
					$result['name'][$_i] = preg_replace($ngReg, '_', $_name);
1762
				}
1763
				if ($error) {
1764
					$result['error'] = $error;
1765
					return $result;
1766
				}
1767
				$result = array_merge_recursive($result, $this->ls($args));
1768
				if (empty($result['list'])) {
1769
					$result['name'] = array();
1770
				} else {
1771
					$result['name'] = array_merge(array_intersect($result['name'], $result['list']));
1772
				}
1773
				return $result;
1774
			}
1775
			if (isset($args['upload']) && is_array($args['upload']) && ($tempDir = $this->getTempDir($volume->getTempPath()))) {
1776
				$names = array();
1777
				foreach($args['upload'] as $i => $url) {
1778
					// check chunked file upload commit
1779
					if ($args['chunk']) {
1780
						if ($url === 'chunkfail' && $args['mimes'] === 'chunkfail') {
1781
							$this->checkChunkedFile(null, $chunk, $cid, $tempDir);
1782
							if (preg_match('/^(.+)(\.\d+_(\d+))\.part$/s', $chunk, $m)) {
1783
								$result['warning'] = $this->error(self::ERROR_UPLOAD_FILE, $m[1], self::ERROR_UPLOAD_TRANSFER);
1784
							}
1785
							return $result;
1786
						} else {
1787
							$tmpfname = $tempDir . '/' . $args['chunk'];
1788
							$files['tmp_name'][$i] = $tmpfname;
1789
							$files['name'][$i] = $url;
1790
							$files['error'][$i] = 0;
1791
							$GLOBALS['elFinderTempFiles'][$tmpfname] = true;
1792
							break;
1793
						}
1794
					}
1795
					
1796
					$tmpfname = $tempDir . DIRECTORY_SEPARATOR . 'ELF_FATCH_' . md5($url.microtime(true));
1797
					
1798
					$_name = '';
1799
					// check is data:
1800
					if (substr($url, 0, 5) === 'data:') {
1801
						list($data, $args['name'][$i]) = $this->parse_data_scheme($url, $extTable);
1802
					} else {
1803
						$fp = fopen($tmpfname, 'wb');
1804
						$data = $this->get_remote_contents($url, 30, 5, 'Mozilla/5.0', $fp);
1805
						$_POST['overwrite'] = false;
1806
						$_name = preg_replace('~^.*?([^/#?]+)(?:\?.*)?(?:#.*)?$~', '$1', rawurldecode($url));
1807
						// Check `Content-Disposition` response header
1808
						if ($data && ($headers = get_headers($url, true)) && !empty($headers['Content-Disposition'])) {
1809
							if (preg_match('/filename\*?=(?:(.+?)\'\')?"?([a-z0-9_.~%-]+)"?/i', $headers['Content-Disposition'], $m)) {
1810
								$_name = rawurldecode($m[2]);
1811
								if ($m[1] && strtoupper($m[1]) !== 'UTF-8' && function_exists('mb_convert_encoding')) {
1812
									$_name = mb_convert_encoding($_name, 'UTF-8', $m[1]);
1813
								}
1814
							}
1815
						}
1816
					}
1817
					if ($data) {
1818
						if (isset($args['name'][$i])) {
1819
							$_name = $args['name'][$i];
1820
						}
1821
						if ($_name) {
1822
							$_ext = '';
1823
							if (preg_match('/(\.[a-z0-9]{1,7})$/', $_name, $_match)) {
1824
								$_ext = $_match[1];
1825
							}
1826
							if ((is_resource($data) && fclose($data)) || file_put_contents($tmpfname, $data)) {
1827
								$GLOBALS['elFinderTempFiles'][$tmpfname] = true;
1828
								$_name = preg_replace($ngReg, '_', $_name);
1829
								list($_a, $_b) = array_pad(explode('.', $_name, 2), 2, '');
1830
								if ($_b === '') {
1831
									if ($_ext) {
1832
										rename($tmpfname, $tmpfname . $_ext);
1833
										$tmpfname = $tmpfname . $_ext;
1834
									}
1835
									$_b = $this->detectFileExtension($tmpfname);
1836
									$_name = $_a.$_b;
1837
								} else {
1838
									$_b = '.'.$_b;
1839
								}
1840
								if (isset($names[$_name])) {
1841
									$_name = $_a.'_'.$names[$_name]++.$_b;
1842
								} else {
1843
									$names[$_name] = 1;
1844
								}
1845
								$files['tmp_name'][$i] = $tmpfname;
1846
								$files['name'][$i] = $_name;
1847
								$files['error'][$i] = 0;
1848
							} else {
1849
								@ unlink($tmpfname);
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...
1850
							}
1851
						}
1852
					}
1853
				}
1854
			}
1855
			if (empty($files)) {
1856
				return array_merge(array('error' => $this->error(self::ERROR_UPLOAD, self::ERROR_UPLOAD_NO_FILES)), $header);
1857
			}
1858
		}
1859
		
1860
		foreach ($files['name'] as $i => $name) {
1861
			if (($error = $files['error'][$i]) > 0) {
1862
				$result['warning'] = $this->error(self::ERROR_UPLOAD_FILE, $name, $error == UPLOAD_ERR_INI_SIZE || $error == UPLOAD_ERR_FORM_SIZE ? self::ERROR_UPLOAD_FILE_SIZE : self::ERROR_UPLOAD_TRANSFER);
1863
				$this->uploadDebug = 'Upload error code: '.$error;
1864
				break;
1865
			}
1866
			
1867
			$tmpname = $files['tmp_name'][$i];
1868
			$path = ($paths && !empty($paths[$i]))? $paths[$i] : '';
1869
			if ($name === 'blob') {
1870
				if ($chunk) {
1871
					if ($tempDir = $this->getTempDir($volume->getTempPath())) {
1872
						list($tmpname, $name) = $this->checkChunkedFile($tmpname, $chunk, $cid, $tempDir, $volume);
1873
						if ($tmpname) {
1874
							if ($name === false) {
1875
								preg_match('/^(.+)(\.\d+_(\d+))\.part$/s', $chunk, $m);
1876
								$result['error'] = $this->error(self::ERROR_UPLOAD_FILE, $m[1], $tmpname);
1877
								$result['_chunkfailure'] = true;
1878
								$this->uploadDebug = 'Upload error: ' . $tmpname;
1879
							} else if ($name) {
1880
								$result['_chunkmerged'] = basename($tmpname);
1881
								$result['_name'] = $name;
1882
							}
1883
						}
1884
					} else {
1885
						$result['error'] = $this->error(self::ERROR_UPLOAD_FILE, $chunk, self::ERROR_UPLOAD_TRANSFER);
1886
						$this->uploadDebug = 'Upload error: unable open tmp file';
1887
					}
1888
					return $result;
1889 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...
1890
					// for form clipboard with Google Chrome
1891
					$type = $files['type'][$i];
1892
					$ext = isset($extTable[$type])? '.' . $extTable[$type] : '';
1893
					$name = substr(md5(basename($tmpname)), 0, 8) . $ext;
1894
				}
1895
			}
1896
			
1897
			// do hook function 'upload.presave'
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% 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...
1898
			if (! empty($this->listeners['upload.presave'])) {
1899 View Code Duplication
				foreach($this->listeners['upload.presave'] as $handler) {
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...
1900
					call_user_func_array($handler, array(&$path, &$name, $tmpname, $this, $volume));
1901
				}
1902
			}
1903
			
1904
			if (($fp = fopen($tmpname, 'rb')) == false) {
1905
				$result['warning'] = $this->error(self::ERROR_UPLOAD_FILE, $name, self::ERROR_UPLOAD_TRANSFER);
1906
				$this->uploadDebug = 'Upload error: unable open tmp file';
1907
				if (! is_uploaded_file($tmpname)) {
1908
					if (@ unlink($tmpname)) unset($GLOBALS['elFinderTempFiles'][$tmpfname]);
1909
					continue;
1910
				}
1911
				break;
1912
			}
1913
			$rnres = array();
1914
			if ($path) {
1915
				$_target = $volume->getUploadTaget($target, $path, $result);
1916
			} else {
1917
				$_target = $target;
1918
				// file rename for backup
1919
				if (isset($renames[$name])) {
1920
					$dir = $volume->realpath($_target);
1921
					$hash = $volume->getHash($dir, $name);
1922
					$rnres = $this->rename(array('target' => $hash, 'name' => $volume->uniqueName($dir, $name, $suffix, true, 0)));
1923
					if (!empty($rnres['error'])) {
1924
						$result['warning'] = $rnres['error'];
1925
						break;
1926
					}
1927
				}
1928
			}
1929
			if (! $_target || ($file = $volume->upload($fp, $_target, $name, $tmpname)) === false) {
1930
				$result['warning'] = $this->error(self::ERROR_UPLOAD_FILE, $name, $volume->error());
1931
				fclose($fp);
1932 View Code Duplication
				if (! is_uploaded_file($tmpname)) {
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...
1933
					if (@ unlink($tmpname)) unset($GLOBALS['elFinderTempFiles'][$tmpname]);;
1934
					continue;
1935
				}
1936
				break;
1937
			}
1938
			
1939
			is_resource($fp) && fclose($fp);
1940
			if (! is_uploaded_file($tmpname)){
1941
				clearstatcache();
1942 View Code Duplication
				if (!is_file($tmpname) || @ unlink($tmpname)) {
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...
1943
					unset($GLOBALS['elFinderTempFiles'][$tmpname]);
1944
				}
1945
			}
1946
			$result['added'][] = $file;
1947
			if ($rnres) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $rnres 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...
1948
				$result = array_merge_recursive($result, $rnres);
1949
			}
1950
		}
1951
		if ($GLOBALS['elFinderTempFiles']) {
1952
			foreach(array_keys($GLOBALS['elFinderTempFiles']) as $_temp) {
1953
				@ unlink($_temp);
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...
1954
			}
1955
		}
1956
		$result['removed'] = $volume->removed();
1957
		
1958
		if (!empty($args['node'])) {
1959
			$result['callback'] = array(
1960
				'node' => $args['node'],
1961
				'bind' => 'upload'
1962
			);
1963
		}
1964
		return $result;
1965
	}
1966
		
1967
	/**
1968
	 * Copy/move files into new destination
1969
	 *
1970
	 * @param  array  command arguments
1971
	 * @return array
1972
	 * @author Dmitry (dio) Levashov
1973
	 **/
1974
	protected function paste($args) {
1975
		$dst     = $args['dst'];
1976
		$targets = is_array($args['targets']) ? $args['targets'] : array();
1977
		$cut     = !empty($args['cut']);
1978
		$error   = $cut ? self::ERROR_MOVE : self::ERROR_COPY;
1979
		$result  = array('added' => array(), 'removed' => array());
1980
		
1981
		if (($dstVolume = $this->volume($dst)) == false) {
1982
			return array('error' => $this->error($error, '#'.$targets[0], self::ERROR_TRGDIR_NOT_FOUND, '#'.$dst));
1983
		}
1984
		
1985
		$renames = array();
1986
		$suffix = '~';
1987
		if (!empty($args['renames'])) {
1988
			$renames = array_flip($args['renames']);
1989 View Code Duplication
			if (is_string($args['suffix']) && ! preg_match('/[\/\\?*:|"<>]/', $args['suffix'])) {
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...
1990
				$suffix = $args['suffix'];
1991
			}
1992
		}
1993
		
1994
		foreach ($targets as $target) {
1995 View Code Duplication
			if (($srcVolume = $this->volume($target)) == 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...
1996
				$result['warning'] = $this->error($error, '#'.$target, self::ERROR_FILE_NOT_FOUND);
1997
				break;
1998
			}
1999
			
2000
			$rnres = array();
2001
			if ($renames) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $renames 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...
2002
				$file = $srcVolume->file($target);
2003
				if (isset($renames[$file['name']])) {
2004
					$dir = $dstVolume->realpath($dst);
2005
					$hash = $dstVolume->getHash($dir, $file['name']);
2006
					$rnres = $this->rename(array('target' => $hash, 'name' => $dstVolume->uniqueName($dir, $file['name'], $suffix, true, 0)));
2007
					if (!empty($rnres['error'])) {
2008
						$result['warning'] = $rnres['error'];
2009
						break;
2010
					}
2011
				}
2012
			}
2013
			
2014
			if (($file = $dstVolume->paste($srcVolume, $target, $dst, $cut)) == false) {
2015
				$result['warning'] = $this->error($dstVolume->error());
2016
				break;
2017
			}
2018
			
2019
			$result['added'][] = $file;
2020
			if ($rnres) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $rnres of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
2021
				$result = array_merge_recursive($result, $rnres);
2022
			}
2023
		}
2024
		return $result;
2025
	}
2026
	
2027
	/**
2028
	 * Return file content
2029
	 *
2030
	 * @param  array  $args  command arguments
2031
	 * @return array
2032
	 * @author Dmitry (dio) Levashov
2033
	 **/
2034
	protected function get($args) {
2035
		$target = $args['target'];
2036
		$volume = $this->volume($target);
2037
		
2038
		if (!$volume || ($file = $volume->file($target)) == false) {
2039
			return array('error' => $this->error(self::ERROR_OPEN, '#'.$target, self::ERROR_FILE_NOT_FOUND));
2040
		}
2041
		
2042 View Code Duplication
		if (($content = $volume->getContents($target)) === 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...
2043
			return array('error' => $this->error(self::ERROR_OPEN, $volume->path($target), $volume->error()));
2044
		}
2045
		
2046
		if ($args['conv'] && function_exists('mb_detect_encoding') && function_exists('mb_convert_encoding')) {
2047
			$mime = isset($file['mime'])? $file['mime'] : '';
2048
			if ($mime && strtolower(substr($mime, 0, 4)) === 'text') {
2049
				if ($enc = mb_detect_encoding ( $content , mb_detect_order(), true)) {
2050
					if (strtolower($enc) !== 'utf-8') {
2051
						$content = mb_convert_encoding($content, 'UTF-8', $enc);
2052
					}
2053
				}
2054
			}
2055
		}
2056
		
2057
		$json = json_encode($content);
2058
2059
		if ($json === false || strlen($json) < strlen($content)) {
2060
			if ($args['conv']) {
2061
				return array('error' => $this->error(self::ERROR_CONV_UTF8,self::ERROR_NOT_UTF8_CONTENT, $volume->path($target)));
2062
			} else {
2063
				return array('doconv' => true);
2064
			}
2065
		}
2066
		
2067
		return array('content' => $content);
2068
	}
2069
	
2070
	/**
2071
	 * Save content into text file
2072
	 *
2073
	 * @return array
2074
	 * @author Dmitry (dio) Levashov
2075
	 **/
2076
	protected function put($args) {
2077
		$target = $args['target'];
2078
		
2079 View Code Duplication
		if (($volume = $this->volume($target)) == 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...
2080
		|| ($file = $volume->file($target)) == false) {
2081
			return array('error' => $this->error(self::ERROR_SAVE, '#'.$target, self::ERROR_FILE_NOT_FOUND));
2082
		}
2083
		
2084 View Code Duplication
		if (($file = $volume->putContents($target, $args['content'])) == 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...
2085
			return array('error' => $this->error(self::ERROR_SAVE, $volume->path($target), $volume->error()));
2086
		}
2087
		
2088
		return array('changed' => array($file));
2089
	}
2090
2091
	/**
2092
	 * Extract files from archive
2093
	 *
2094
	 * @param  array  $args  command arguments
2095
	 * @return array
2096
	 * @author Dmitry (dio) Levashov, 
2097
	 * @author Alexey Sukhotin
2098
	 **/
2099
	protected function extract($args) {
2100
		$target = $args['target'];
2101
		$mimes  = !empty($args['mimes']) && is_array($args['mimes']) ? $args['mimes'] : array();
0 ignored issues
show
Unused Code introduced by
$mimes 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...
2102
		$error  = array(self::ERROR_EXTRACT, '#'.$target);
0 ignored issues
show
Unused Code introduced by
$error 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...
2103
		$makedir = isset($args['makedir'])? (bool)$args['makedir'] : null;
2104
2105 View Code Duplication
		if (($volume = $this->volume($target)) == 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...
2106
		|| ($file = $volume->file($target)) == false) {
2107
			return array('error' => $this->error(self::ERROR_EXTRACT, '#'.$target, self::ERROR_FILE_NOT_FOUND));
2108
		}  
2109
2110
		return ($file = $volume->extract($target, $makedir))
2111
			? array('added' => isset($file['read'])? array($file) : $file)
2112
			: array('error' => $this->error(self::ERROR_EXTRACT, $volume->path($target), $volume->error()));
2113
	}
2114
	
2115
	/**
2116
	 * Create archive
2117
	 *
2118
	 * @param  array  $args  command arguments
2119
	 * @return array
2120
	 * @author Dmitry (dio) Levashov, 
2121
	 * @author Alexey Sukhotin
2122
	 **/
2123
	protected function archive($args) {
2124
		$type    = $args['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...
2125
		$targets = isset($args['targets']) && is_array($args['targets']) ? $args['targets'] : array();
2126
		$name    = isset($args['name'])? $args['name'] : '';
2127
	
2128 View Code Duplication
		if (($volume = $this->volume($targets[0])) == 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...
2129
			return $this->error(self::ERROR_ARCHIVE, self::ERROR_TRGDIR_NOT_FOUND);
2130
		}
2131
	
2132
		return ($file = $volume->archive($targets, $args['type'], $name))
2133
			? array('added' => array($file))
2134
			: array('error' => $this->error(self::ERROR_ARCHIVE, $volume->error()));
2135
	}
2136
	
2137
	/**
2138
	 * Search files
2139
	 *
2140
	 * @param  array  $args  command arguments
2141
	 * @return array
2142
	 * @author Dmitry Levashov
2143
	 **/
2144
	protected function search($args) {
2145
		$q      = trim($args['q']);
2146
		$mimes  = !empty($args['mimes']) && is_array($args['mimes']) ? $args['mimes'] : array();
2147
		$target = !empty($args['target'])? $args['target'] : null;
2148
		$result = array();
2149
		$errors = array();
2150
2151
		if ($target) {
2152 View Code Duplication
			if ($volume = $this->volume($target)) {
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...
2153
				$result = $volume->search($q, $mimes, $target);
2154
				$errors = array_merge($errors, $volume->error());
2155
			}
2156 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...
2157
			foreach ($this->volumes as $volume) {
2158
				$result = array_merge($result, $volume->search($q, $mimes));
2159
				$errors = array_merge($errors, $volume->error());
2160
			}
2161
		}
2162
		
2163
		$result = array('files' => $result);
2164
		if ($errors) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $errors 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...
2165
			$result['warning'] = $errors;
2166
		}
2167
		return $result;
2168
	}
2169
	
2170
	/**
2171
	 * Return file info (used by client "places" ui)
2172
	 *
2173
	 * @param  array  $args  command arguments
2174
	 * @return array
2175
	 * @author Dmitry Levashov
2176
	 **/
2177
	protected function info($args) {
2178
		$files = array();
2179
		$sleep = 0;
0 ignored issues
show
Unused Code introduced by
$sleep 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...
2180
		$compare = null;
2181
		// long polling mode
2182
		if ($args['compare'] && count($args['targets']) === 1) {
2183
			$compare = intval($args['compare']);
2184
			$hash = $args['targets'][0];
2185
			if ($volume = $this->volume($hash)) {
2186
				$standby = (int)$volume->getOption('plStandby');
2187
				$_compare = false;
2188
				if (($syncCheckFunc = $volume->getOption('syncCheckFunc')) && is_callable($syncCheckFunc)) {
2189
					$_compare = call_user_func_array($syncCheckFunc, array($volume->realpath($hash), $standby, $compare, $volume, $this));
2190
				}
2191
				if ($_compare !== false) {
2192
					$compare = $_compare;
2193
				} else {
2194
					$sleep = max(1, (int)$volume->getOption('tsPlSleep'));
2195
					$limit = max(1, $standby / $sleep) + 1;
2196
					$timelimit = ini_get('max_execution_time');
2197
					do {
2198
						$timelimit && @ set_time_limit($timelimit + $sleep);
2199
						$volume->clearstatcache();
2200
						if (($info = $volume->file($hash)) != false) {
2201
							if ($info['ts'] != $compare) {
2202
								$compare = $info['ts'];
2203
								break;
2204
							}
2205
						} else {
2206
							$compare = 0;
2207
							break;
2208
						}
2209
						if (--$limit) {
2210
							sleep($sleep);
2211
						}
2212
					} while($limit);
2213
				}
2214
			}
2215
		} else {
2216
			foreach ($args['targets'] as $hash) {
2217
				if (($volume = $this->volume($hash)) != false
2218
				&& ($info = $volume->file($hash)) != false) {
2219
					$files[] = $info;
2220
				}
2221
			}
2222
		}
2223
		
2224
		$result = array('files' => $files);
2225
		if (!is_null($compare)) {
2226
			$result['compare'] = strval($compare);
2227
		}
2228
		return $result;
2229
	}
2230
	
2231
	/**
2232
	 * Return image dimmensions
2233
	 *
2234
	 * @param  array  $args  command arguments
2235
	 * @return array
2236
	 * @author Dmitry (dio) Levashov
2237
	 **/
2238
	protected function dim($args) {
2239
		$target = $args['target'];
2240
		
2241
		if (($volume = $this->volume($target)) != false) {
2242
			$dim = $volume->dimensions($target);
2243
			return $dim ? array('dim' => $dim) : array();
2244
		}
2245
		return array();
2246
	}
2247
	
2248
	/**
2249
	 * Resize image
2250
	 *
2251
	 * @param  array  command arguments
2252
	 * @return array
2253
	 * @author Dmitry (dio) Levashov
2254
	 * @author Alexey Sukhotin
2255
	 **/
2256
	protected function resize($args) {
2257
		$target = $args['target'];
2258
		$width  = $args['width'];
2259
		$height = $args['height'];
2260
		$x      = (int)$args['x'];
2261
		$y      = (int)$args['y'];
2262
		$mode   = $args['mode'];
2263
		$bg     = null;
2264
		$degree = (int)$args['degree'];
2265
		$quality= (int)$args['quality'];
2266
		
2267 View Code Duplication
		if (($volume = $this->volume($target)) == 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...
2268
		|| ($file = $volume->file($target)) == false) {
2269
			return array('error' => $this->error(self::ERROR_RESIZE, '#'.$target, self::ERROR_FILE_NOT_FOUND));
2270
		}
2271
2272
		return ($file = $volume->resize($target, $width, $height, $x, $y, $mode, $bg, $degree, $quality))
2273
			? array('changed' => array($file))
2274
			: array('error' => $this->error(self::ERROR_RESIZE, $volume->path($target), $volume->error()));
2275
	}
2276
	
2277
	/**
2278
	* Return content URL
2279
	*
2280
	* @param  array  $args  command arguments
2281
	* @return array
2282
	* @author Naoki Sawada
2283
	**/
2284
	protected function url($args) {
2285
		$target = $args['target'];
2286
		$options = isset($args['options'])? $args['options'] : array();
2287
		if (($volume = $this->volume($target)) != false) {
2288
			$url = $volume->getContentUrl($target, $options);
2289
			return $url ? array('url' => $url) : array();
2290
		}
2291
		return array();
2292
	}
2293
2294
	/**
2295
	 * Output callback result with JavaScript that control elFinder
2296
	 * or HTTP redirect to callbackWindowURL
2297
	 * 
2298
	 * @param  array  command arguments
2299
	 * @author Naoki Sawada
2300
	 */
2301
	protected function callback($args) {
2302
		$checkReg = '/[^a-zA-Z0-9;._-]/';
2303
		$node = (isset($args['node']) && !preg_match($checkReg, $args['node']))? $args['node'] : '';
2304
		$json = (isset($args['json']) && @json_decode($args['json']))? $args['json'] : '{}';
2305
		$bind  = (isset($args['bind']) && !preg_match($checkReg, $args['bind']))? $args['bind'] : '';
2306
		$done = (!empty($args['done']));
2307
		
2308
		while( ob_get_level() ) {
2309
			if (! ob_end_clean()) {
2310
				break;
2311
			}
2312
		}
2313
		
2314
		if ($done || ! $this->callbackWindowURL) {
2315
			$script = '';
2316
			if ($node) {
2317
				$script .= '
2318
					var w = window.opener || window.parent || window;
2319
					try {
2320
						var elf = w.document.getElementById(\''.$node.'\').elfinder;
2321
						if (elf) {
2322
							var data = '.$json.';
2323
							if (data.error) {
2324
								elf.error(data.error);
2325
							} else {
2326
								data.warning && elf.error(data.warning);
2327
								data.removed && data.removed.length && elf.remove(data);
2328
								data.added   && data.added.length   && elf.add(data);
2329
								data.changed && data.changed.length && elf.change(data);';
2330
				if ($bind) {
2331
					$script .= '
2332
								elf.trigger(\''.$bind.'\', data);';
2333
				}
2334
				$script .= '
2335
								data.sync && elf.sync();
2336
							}
2337
						}
2338
					} catch(e) {
2339
						// for CORS
2340
						w.postMessage && w.postMessage(JSON.stringify({bind:\''.$bind.'\',data:'.$json.'}), \'*\');
2341
					}';
2342
			}
2343
			$script .= 'window.close();';
2344
			
2345
			$out = '<!DOCTYPE html><html><head><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><script>'.$script.'</script></head><body><a href="#" onlick="window.close();return false;">Close this window</a></body></html>';
2346
			
2347
			header('Content-Type: text/html; charset=utf-8');
2348
			header('Content-Length: '.strlen($out));
2349
			header('Cache-Control: private');
2350
			header('Pragma: no-cache');
2351
			
2352
			echo $out;
2353
			
2354
		} else {
2355
			$url = $this->callbackWindowURL;
2356
			$url .= ((strpos($url, '?') === false)? '?' : '&')
2357
				 . '&node=' . rawurlencode($node)
2358
				 . (($json !== '{}')? ('&json=' . rawurlencode($json)) : '')
2359
				 . ($bind? ('&bind=' .  rawurlencode($bind)) : '')
2360
				 . '&done=1';
2361
			
2362
			header('Location: ' . $url);
2363
			
2364
		}
2365
		exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The method callback() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
2366
	}
2367
2368
	/***************************************************************************/
2369
	/*                                   utils                                 */
2370
	/***************************************************************************/
2371
	
2372
	/**
2373
	 * Return root - file's owner
2374
	 *
2375
	 * @param  string  file hash
2376
	 * @return elFinderStorageDriver
2377
	 * @author Dmitry (dio) Levashov
2378
	 **/
2379
	protected function volume($hash) {
2380
		foreach ($this->volumes as $id => $v) {
2381
			if (strpos(''.$hash, $id) === 0) {
2382
				return $this->volumes[$id];
2383
			} 
2384
		}
2385
		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 elFinder::volume of type elFinderStorageDriver.

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...
2386
	}
2387
	
2388
	/**
2389
	 * Return files info array 
2390
	 *
2391
	 * @param  array  $data  one file info or files info
2392
	 * @return array
2393
	 * @author Dmitry (dio) Levashov
2394
	 **/
2395
	protected function toArray($data) {
2396
		return isset($data['hash']) || !is_array($data) ? array($data) : $data;
2397
	}
2398
	
2399
	/**
2400
	 * Return fils hashes list
2401
	 *
2402
	 * @param  array  $files  files info
2403
	 * @return array
2404
	 * @author Dmitry (dio) Levashov
2405
	 **/
2406
	protected function hashes($files) {
2407
		$ret = array();
2408
		foreach ($files as $file) {
2409
			$ret[] = $file['hash'];
2410
		}
2411
		return $ret;
2412
	}
2413
	
2414
	/**
2415
	 * Remove from files list hidden files and files with required mime types
2416
	 *
2417
	 * @param  array  $files  files info
2418
	 * @return array
2419
	 * @author Dmitry (dio) Levashov
2420
	 **/
2421
	protected function filter($files) {
2422
		foreach ($files as $i => $file) {
2423
			if (!empty($file['hidden']) || !$this->default->mimeAccepted($file['mime'])) {
2424
				unset($files[$i]);
2425
			}
2426
		}
2427
		return array_merge($files, array());
2428
	}
2429
	
2430
	protected function utime() {
2431
		$time = explode(" ", microtime());
2432
		return (double)$time[1] + (double)$time[0];
2433
	}
2434
	
2435
	
2436
	/***************************************************************************/
2437
	/*                           static  utils                                 */
2438
	/***************************************************************************/
2439
	
2440
	/**
2441
	 * Return Is Animation Gif
2442
	 * 
2443
	 * @param  string $path server local path of target image
2444
	 * @return bool
2445
	 */
2446
	public static function isAnimationGif($path) {
2447
		list($width, $height, $type, $attr) = getimagesize($path);
0 ignored issues
show
Unused Code introduced by
The assignment to $width is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
Unused Code introduced by
The assignment to $height is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
Unused Code introduced by
The assignment to $attr is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
2448
		switch ($type) {
2449
			case IMAGETYPE_GIF:
2450
				break;
2451
			default:
2452
				return false;
2453
		}
2454
	
2455
		$imgcnt = 0;
2456
		$fp = fopen($path, 'rb');
2457
		@fread($fp, 4);
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...
2458
		$c = @fread($fp,1);
2459
		if (ord($c) != 0x39) {  // GIF89a
2460
			return false;
2461
		}
2462
	
2463
		while (!feof($fp)) {
2464
			do {
2465
				$c = fread($fp, 1);
2466
			} while(ord($c) != 0x21 && !feof($fp));
2467
	
2468
			if (feof($fp)) {
2469
				break;
2470
			}
2471
	
2472
			$c2 = fread($fp,2);
2473
			if (bin2hex($c2) == "f904") {
2474
				$imgcnt++;
2475
			}
2476
	
2477
			if (feof($fp)) {
2478
				break;
2479
			}
2480
		}
2481
	
2482
		if ($imgcnt > 1) {
2483
			return true;
2484
		} else {
2485
			return false;
2486
		}
2487
	}
2488
2489
	/**
2490
	 * Return Is seekable stream resource
2491
	 * 
2492
	 * @param resource $resource
2493
	 * @return bool
2494
	 */
2495
	public static function isSeekableStream($resource) {
2496
		$metadata = stream_get_meta_data($resource);
2497
		return $metadata['seekable'];
2498
	}
2499
2500
	/**
2501
	 * serialize and base64_encode of session data (If needed)
2502
	 * 
2503
	 * @param  mixed $var  target variable
2504
	 * @author Naoki Sawada
2505
	 */
2506
	public static function sessionDataEncode($var) {
2507
		if (self::$base64encodeSessionData) {
2508
			$var = base64_encode(serialize($var));
2509
		}
2510
		return $var;
2511
	}
2512
	
2513
	/**
2514
	 * base64_decode and unserialize of session data  (If needed)
2515
	 * 
2516
	 * @param  mixed $var      target variable
2517
	 * @param  bool  $checkIs  data type for check (array|string|object|int)
2518
	 * @author Naoki Sawada
2519
	 */
2520
	public static function sessionDataDecode(&$var, $checkIs = null) {
2521
		if (self::$base64encodeSessionData) {
2522
			$data = @unserialize(@base64_decode($var));
2523
		} else {
2524
			$data = $var;
2525
		}
2526
		$chk = true;
2527
		if ($checkIs) {
2528
			switch ($checkIs) {
2529
				case 'array':
2530
					$chk = is_array($data);
2531
					break;
2532
				case 'string':
2533
					$chk = is_string($data);
2534
					break;
2535
				case 'object':
2536
					$chk = is_object($data);
2537
					break;
2538
				case 'int':
2539
					$chk = is_int($data);
2540
					break;
2541
			}
2542
		}
2543
		if (!$chk) {
2544
			unset($var);
2545
			return false;
2546
		}
2547
		return $data;
2548
	}
2549
} // END class
2550