Completed
Push — 2.x ( 886a3c...56906b )
by Naoki
03:06
created

elFinder::file()   D

Complexity

Conditions 19
Paths 212

Size

Total Lines 74
Code Lines 52

Duplication

Lines 9
Ratio 12.16 %
Metric Value
dl 9
loc 74
rs 4.7305
cc 19
eloc 52
nc 212
nop 1

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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),
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),
106
		'dim'       => array('target' => true),
107
		'resize'    => array('target' => true, 'width' => true, 'height' => true, 'mode' => false, 'x' => false, 'y' => false, 'degree' => 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 default is `netmount`, `netunmount` @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
261
	/**
262
	 * Constructor
263
	 *
264
	 * @param  array  elFinder and roots configurations
265
	 * @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...
266
	 * @author Dmitry (dio) Levashov
267
	 **/
268
	public function __construct($opts) {
0 ignored issues
show
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 $_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 $_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...
269
		if (session_id() == '') {
270
			session_start();
271
		}
272
		$sessionUseCmds = array('netmount', 'netunmount');
273
		if (isset($opts['sessionUseCmds']) && is_array($opts['sessionUseCmds'])) {
274
			$sessionUseCmds = array_merge($sessionUseCmds, $opts['sessionUseCmds']);
275
		}
276
277
		// set self::$volumesCnt by HTTP header "X-elFinder-VolumesCntStart"
278
		if (isset($_SERVER['HTTP_X_ELFINDER_VOLUMESCNTSTART']) && ($volumesCntStart = intval($_SERVER['HTTP_X_ELFINDER_VOLUMESCNTSTART']))) {
279
			self::$volumesCnt = $volumesCntStart;
280
		}
281
		
282
		$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...
283
		$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...
284
		$this->sessionCloseEarlier = isset($opts['sessionCloseEarlier'])? (bool)$opts['sessionCloseEarlier'] : true;
285
		$this->sessionUseCmds = array_flip($sessionUseCmds);
286
		$this->timeout = (isset($opts['timeout']) ? $opts['timeout'] : 0);
287
		$this->uploadTempPath = (isset($opts['uploadTempPath']) ? $opts['uploadTempPath'] : '');
288
		$this->netVolumesSessionKey = !empty($opts['netVolumesSessionKey'])? $opts['netVolumesSessionKey'] : 'elFinderNetVolumes';
289
		$this->callbackWindowURL = (isset($opts['callbackWindowURL']) ? $opts['callbackWindowURL'] : '');
290
		self::$sessionCacheKey = !empty($opts['sessionCacheKey']) ? $opts['sessionCacheKey'] : 'elFinderCaches';
291
		
292
		// check session cache
293
		$_optsMD5 = md5(json_encode($opts['roots']));
294
		if (! isset($_SESSION[self::$sessionCacheKey]) || $_SESSION[self::$sessionCacheKey]['_optsMD5'] !== $_optsMD5) {
295
			$_SESSION[self::$sessionCacheKey] = array(
296
				'_optsMD5' => $_optsMD5
297
			);
298
		}
299
		self::$base64encodeSessionData = !empty($opts['base64encodeSessionData']);
300
		
301
		// setlocale and global locale regists to elFinder::locale
302
		self::$locale = !empty($opts['locale']) ? $opts['locale'] : 'en_US.UTF-8';
303
		if (false === @setlocale(LC_ALL, self::$locale)) {
304
			self::$locale = setlocale(LC_ALL, '');
305
		}
306
307
		// bind events listeners
308
		if (!empty($opts['bind']) && is_array($opts['bind'])) {
309
			$_req = $_SERVER["REQUEST_METHOD"] == 'POST' ? $_POST : $_GET;
310
			$_reqCmd = isset($_req['cmd']) ? $_req['cmd'] : '';
311
			foreach ($opts['bind'] as $cmd => $handlers) {
312
				$doRegist = (strpos($cmd, '*') !== false);
313
				if (! $doRegist) {
314
					$_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...
315
					$doRegist = ($_reqCmd && in_array($_reqCmd, array_map($_getcmd, explode(' ', $cmd))));
316
				}
317
				if ($doRegist) {
318
					if (! is_array($handlers) || is_object($handlers[0])) {
319
						$handlers = array($handlers);
320
					}
321
					foreach($handlers as $handler) {
322
						if ($handler) {
323
							if (is_string($handler) && strpos($handler, '.')) {
324
								list($_domain, $_name, $_method) = array_pad(explode('.', $handler), 3, '');
325
								if (strcasecmp($_domain, 'plugin') === 0) {
326
									if ($plugin = $this->getPluginInstance($_name, isset($opts['plugin'][$_name])? $opts['plugin'][$_name] : array())
327
											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...
328
										$this->bind($cmd, array($plugin, $_method));
329
									}
330
								}
331
							} else {
332
								$this->bind($cmd, $handler);
333
							}
334
						}
335
					}
336
				}
337
			}
338
		}
339
340
		if (!isset($opts['roots']) || !is_array($opts['roots'])) {
341
			$opts['roots'] = array();
342
		}
343
344
		// check for net volumes stored in session
345
		foreach ($this->getNetVolumes() as $key => $root) {
346
			$opts['roots'][$key] = $root;
347
		}
348
349
		// "mount" volumes
350
		foreach ($opts['roots'] as $i => $o) {
351
			$class = 'elFinderVolume'.(isset($o['driver']) ? $o['driver'] : '');
352
353
			if (class_exists($class)) {
354
				$volume = new $class();
355
356
				try {
357
					if ($volume->mount($o)) {
358
						// unique volume id (ends on "_") - used as prefix to files hash
359
						$id = $volume->id();
360
						
361
						$this->volumes[$id] = $volume;
362
						if (!$this->default && $volume->isReadable()) {
363
							$this->default = $this->volumes[$id]; 
364
						}
365
					} else {
366
						$this->removeNetVolume($i);
367
						$this->mountErrors[] = 'Driver "'.$class.'" : '.implode(' ', $volume->error());
368
					}
369
				} catch (Exception $e) {
370
					$this->removeNetVolume($i);
371
					$this->mountErrors[] = 'Driver "'.$class.'" : '.$e->getMessage();
372
				}
373
			} else {
374
				$this->mountErrors[] = 'Driver "'.$class.'" does not exists';
375
			}
376
		}
377
378
		// if at least one readable volume - ii desu >_<
379
		$this->loaded = !empty($this->default);
380
	}
381
	
382
	/**
383
	 * Return true if fm init correctly
384
	 *
385
	 * @return bool
386
	 * @author Dmitry (dio) Levashov
387
	 **/
388
	public function loaded() {
389
		return $this->loaded;
390
	}
391
	
392
	/**
393
	 * Return version (api) number
394
	 *
395
	 * @return string
396
	 * @author Dmitry (dio) Levashov
397
	 **/
398
	public function version() {
399
		return $this->version;
400
	}
401
	
402
	/**
403
	 * Add handler to elFinder command
404
	 *
405
	 * @param  string  command name
406
	 * @param  string|array  callback name or array(object, method)
407
	 * @return elFinder
408
	 * @author Dmitry (dio) Levashov
409
	 **/
410
	public function bind($cmd, $handler) {
411
		$allCmds = array_keys($this->commands);
412
		$cmds = array();
413
		foreach(explode(' ', $cmd) as $_cmd) {
414
			if ($_cmd !== '') {
415
				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...
416
					list(, $sub) = array_pad(explode('.', $_cmd), 2, '');
417
					if ($sub) {
418
						$sub = str_replace('\'', '\\\'', $sub);
419
						$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...
420
						$cmds = array_merge($cmds, array_map($addSub, $allCmds));
421
					} else {
422
						$cmds = array_merge($cmds, $allCmds);
423
					}
424
				} else {
425
					$cmds[] = $_cmd;
426
				}
427
			}
428
		}
429
		$cmds = array_unique($cmds);
430
		
431
		foreach ($cmds as $cmd) {
432
			if (!isset($this->listeners[$cmd])) {
433
				$this->listeners[$cmd] = array();
434
			}
435
436
			if (is_callable($handler)) {
437
				$this->listeners[$cmd][] = $handler;
438
			}
439
		}
440
441
		return $this;
442
	}
443
	
444
	/**
445
	 * Remove event (command exec) handler
446
	 *
447
	 * @param  string  command name
448
	 * @param  string|array  callback name or array(object, method)
449
	 * @return elFinder
450
	 * @author Dmitry (dio) Levashov
451
	 **/
452
	public function unbind($cmd, $handler) {
453
		if (!empty($this->listeners[$cmd])) {
454
			foreach ($this->listeners[$cmd] as $i => $h) {
455
				if ($h === $handler) {
456
					unset($this->listeners[$cmd][$i]);
457
					return $this;
458
				}
459
			}
460
		}
461
		return $this;
462
	}
463
	
464
	/**
465
	 * Return true if command exists
466
	 *
467
	 * @param  string  command name
468
	 * @return bool
469
	 * @author Dmitry (dio) Levashov
470
	 **/
471
	public function commandExists($cmd) {
472
		return $this->loaded && isset($this->commands[$cmd]) && method_exists($this, $cmd);
473
	}
474
	
475
	/**
476
	 * Return root - file's owner (public func of volume())
477
	 *
478
	 * @param  string  file hash
479
	 * @return elFinderStorageDriver
480
	 * @author Naoki Sawada
481
	 */
482
	public function getVolume($hash) {
483
		return $this->volume($hash);
484
	}
485
	
486
	/**
487
	 * Return command required arguments info
488
	 *
489
	 * @param  string  command name
490
	 * @return array
491
	 * @author Dmitry (dio) Levashov
492
	 **/
493
	public function commandArgsList($cmd) {
494
		return $this->commandExists($cmd) ? $this->commands[$cmd] : array();
495
	}
496
497
	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...
498
		
499
		if (!isset($_SESSION[self::$sessionCacheKey . ':LAST_ACTIVITY'])) {
500
			$_SESSION[self::$sessionCacheKey . ':LAST_ACTIVITY'] = time();
501
			return false;
502
		}
503
504
		if ( ($this->timeout > 0) && (time() - $_SESSION[self::$sessionCacheKey . ':LAST_ACTIVITY'] > $this->timeout) ) {
505
			return true;
506
		}
507
508
		$_SESSION[self::$sessionCacheKey . ':LAST_ACTIVITY'] = time();
509
		return false;	
510
	}
511
	
512
	/**
513
	 * Exec command and return result
514
	 *
515
	 * @param  string  $cmd  command name
516
	 * @param  array   $args command arguments
517
	 * @return array
518
	 * @author Dmitry (dio) Levashov
519
	 **/
520
	public function exec($cmd, $args) {
521
		
522
		if (!$this->loaded) {
523
			return array('error' => $this->error(self::ERROR_CONF, self::ERROR_CONF_NO_VOL));
524
		}
525
526
		if ($this->session_expires()) {
527
			return array('error' => $this->error(self::ERROR_SESSION_EXPIRES));
528
		}
529
		
530
		if (!$this->commandExists($cmd)) {
531
			return array('error' => $this->error(self::ERROR_UNKNOWN_CMD));
532
		}
533
		
534
		if (!empty($args['mimes']) && is_array($args['mimes'])) {
535
			foreach ($this->volumes as $id => $v) {
536
				$this->volumes[$id]->setMimesFilter($args['mimes']);
537
			}
538
		}
539
540
		// call pre handlers for this command
541
		$args['sessionCloseEarlier'] = isset($this->sessionUseCmds[$cmd])? false : $this->sessionCloseEarlier;
542
		if (!empty($this->listeners[$cmd.'.pre'])) {
543
			$volume = isset($args['target'])? $this->volume($args['target']) : false;
544 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...
545
				call_user_func_array($handler, array($cmd, &$args, $this, $volume));
546
			}
547
		}
548
		
549
		// unlock session data for multiple access
550
		$this->sessionCloseEarlier && $args['sessionCloseEarlier'] && session_id() && session_write_close();
551
		unset($this->sessionCloseEarlier);
552
		
553
		$result = $this->$cmd($args);
554
		
555
		if (isset($result['removed'])) {
556
			foreach ($this->volumes as $volume) {
557
				$result['removed'] = array_merge($result['removed'], $volume->removed());
558
				$volume->resetRemoved();
559
			}
560
		}
561
		
562
		// call handlers for this command
563
		if (!empty($this->listeners[$cmd])) {
564
			foreach ($this->listeners[$cmd] as $handler) {
565
				if (call_user_func_array($handler,array($cmd,&$result,$args,$this))) {
566
					// handler return true to force sync client after command completed
567
					$result['sync'] = true;
568
				}
569
			}
570
		}
571
		
572
		// replace removed files info with removed files hashes
573
		if (!empty($result['removed'])) {
574
			$removed = array();
575
			foreach ($result['removed'] as $file) {
576
				$removed[] = $file['hash'];
577
			}
578
			$result['removed'] = array_unique($removed);
579
		}
580
		// remove hidden files and filter files by mimetypes
581
		if (!empty($result['added'])) {
582
			$result['added'] = $this->filter($result['added']);
583
		}
584
		// remove hidden files and filter files by mimetypes
585
		if (!empty($result['changed'])) {
586
			$result['changed'] = $this->filter($result['changed']);
587
		}
588
		
589
		if ($this->debug || !empty($args['debug'])) {
590
			$result['debug'] = array(
591
				'connector' => 'php', 
592
				'phpver'    => PHP_VERSION,
593
				'time'      => $this->utime() - $this->time,
594
				'memory'    => (function_exists('memory_get_peak_usage') ? ceil(memory_get_peak_usage()/1024).'Kb / ' : '').ceil(memory_get_usage()/1024).'Kb / '.ini_get('memory_limit'),
595
				'upload'    => $this->uploadDebug,
596
				'volumes'   => array(),
597
				'mountErrors' => $this->mountErrors
598
				);
599
			
600
			foreach ($this->volumes as $id => $volume) {
601
				$result['debug']['volumes'][] = $volume->debug();
602
			}
603
		}
604
		
605
		foreach ($this->volumes as $volume) {
606
			$volume->umount();
607
		}
608
		
609
		if (!empty($result['callback'])) {
610
			$result['callback']['json'] = json_encode($result);
611
			$this->callback($result['callback']);
612
		} else {
613
			return $result;
614
		}
615
	}
616
	
617
	/**
618
	 * Return file real path
619
	 *
620
	 * @param  string  $hash  file hash
621
	 * @return string
622
	 * @author Dmitry (dio) Levashov
623
	 **/
624
	public function realpath($hash)	{
625
		if (($volume = $this->volume($hash)) == false) {
626
			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...
627
		}
628
		return $volume->realpath($hash);
629
	}
630
	
631
	/**
632
	 * Return network volumes config.
633
	 *
634
	 * @return array
635
	 * @author Dmitry (dio) Levashov
636
	 */
637
	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...
638
		if (isset($_SESSION[$this->netVolumesSessionKey])) {
639
			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...
640
				return $data;
641
			}
642
		}
643
		return array();
644
	}
645
646
	/**
647
	 * Save network volumes config.
648
	 *
649
	 * @param  array  $volumes  volumes config
650
	 * @return void
651
	 * @author Dmitry (dio) Levashov
652
	 */
653
	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...
654
		$_SESSION[$this->netVolumesSessionKey] = elFinder::sessionDataEncode($volumes);
655
	}
656
657
	/**
658
	 * Remove netmount volume
659
	 * 
660
	 * @param string $key  netvolume key
661
	 */
662
	protected function removeNetVolume($key) {
663
		$netVolumes = $this->getNetVolumes();
664
		if (is_string($key) && isset($netVolumes[$key])) {
665
			unset($netVolumes[$key]);
666
			$this->saveNetVolumes($netVolumes);
667
		}
668
	}
669
670
	/**
671
	 * Get plugin instance & set to $this->plugins
672
	 * 
673
	 * @param  string $name   Plugin name (dirctory name)
674
	 * @param  array  $opts   Plugin options (optional)
675
	 * @return object | bool Plugin object instance Or false
676
	 * @author Naoki Sawada
677
	 */
678
	protected function getPluginInstance($name, $opts = array()) {
679
		$key = strtolower($name);
680
		if (! isset($this->plugins[$key])) {
681
			$p_file = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . $name . DIRECTORY_SEPARATOR . 'plugin.php';
682
			if (is_file($p_file)) {
683
				require_once $p_file;
684
				$class = 'elFinderPlugin' . $name;
685
				$this->plugins[$key] = new $class($opts);
686
			} else {
687
				$this->plugins[$key] = false;
688
			}
689
		}
690
		return $this->plugins[$key];
691
	}
692
693
	/***************************************************************************/
694
	/*                                 commands                                */
695
	/***************************************************************************/
696
	
697
	/**
698
	 * Normalize error messages
699
	 *
700
	 * @return array
701
	 * @author Dmitry (dio) Levashov
702
	 **/
703
	public function error() {
704
		$errors = array();
705
706
		foreach (func_get_args() as $msg) {
707
			if (is_array($msg)) {
708
				$errors = array_merge($errors, $msg);
709
			} else {
710
				$errors[] = $msg;
711
			}
712
		}
713
		
714
		return count($errors) ? $errors : array(self::ERROR_UNKNOWN);
715
	}
716
	
717
	protected function netmount($args) {
718
		$options  = array();
719
		$protocol = $args['protocol'];
720
		
721
		if ($protocol === 'netunmount') {
722
			$key = $args['host'];
723
			$netVolumes = $this->getNetVolumes();
724
			if ($netVolumes[$key]) {
725
				$res = true;
726
				$volume = $this->volume($args['user']);
727
				if (method_exists($volume, 'netunmount')) {
728
					$res = $volume->netunmount($netVolumes, $key);
729
				}
730
				if ($res) {
731
					unset($netVolumes[$key]);
732
					$this->saveNetVolumes($netVolumes);
733
					return array('sync' => true);
734
				}
735
			}
736
			return array('error' => $this->error(self::ERROR_NETUNMOUNT));
737
		}
738
		
739
		$driver   = isset(self::$netDrivers[$protocol]) ? self::$netDrivers[$protocol] : '';
740
		$class    = 'elfindervolume'.$driver;
741
742
		if (!class_exists($class)) {
743
			return array('error' => $this->error(self::ERROR_NETMOUNT, $args['host'], self::ERROR_NETMOUNT_NO_DRIVER));
744
		}
745
746
		if (!$args['path']) {
747
			$args['path'] = '/';
748
		}
749
750
		foreach ($args as $k => $v) {
751
			if ($k != 'options' && $k != 'protocol' && $v) {
752
				$options[$k] = $v;
753
			}
754
		}
755
756
		if (is_array($args['options'])) {
757
			foreach ($args['options'] as $key => $value) {
758
				$options[$key] = $value;
759
			}
760
		}
761
762
		$volume = new $class();
763
		
764
		if (method_exists($volume, 'netmountPrepare')) {
765
			$options = $volume->netmountPrepare($options);
766
			if (isset($options['exit'])) {
767
				if ($options['exit'] === 'callback') {
768
					$this->callback($options['out']);
769
				}
770
				return $options;
771
			}
772
		}
773
		
774
		$netVolumes = $this->getNetVolumes();
775
		if ($volume->mount($options)) {
776
			if (! $key = @ $volume->netMountKey) {
777
				$key = md5($protocol . '-' . join('-', $options));
778
			}
779
			$options['driver'] = $driver;
780
			$options['netkey'] = $key;
781
			$netVolumes[$key]  = $options;
782
			$this->saveNetVolumes($netVolumes);
783
			$rootstat = $volume->file($volume->root());
784
			$rootstat['netkey'] = $key;
785
			return array('added' => array($rootstat));
786
		} else {
787
			$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...
788
			return array('error' => $this->error(self::ERROR_NETMOUNT, $args['host'], implode(' ', $volume->error())));
789
		}
790
791
	}
792
793
	/**
794
	 * "Open" directory
795
	 * Return array with following elements
796
	 *  - cwd          - opened dir info
797
	 *  - files        - opened dir content [and dirs tree if $args[tree]]
798
	 *  - api          - api version (if $args[init])
799
	 *  - uplMaxSize   - if $args[init]
800
	 *  - error        - on failed
801
	 *
802
	 * @param  array  command arguments
803
	 * @return array
804
	 * @author Dmitry (dio) Levashov
805
	 **/
806
	protected function open($args) {
807
		$target = $args['target'];
808
		$init   = !empty($args['init']);
809
		$tree   = !empty($args['tree']);
810
		$volume = $this->volume($target);
811
		$cwd    = $volume ? $volume->dir($target) : false;
812
		$hash   = $init ? 'default folder' : '#'.$target;
813
814
		// on init request we can get invalid dir hash -
815
		// dir which can not be opened now, but remembered by client,
816
		// so open default dir
817
		if ((!$cwd || !$cwd['read']) && $init) {
818
			$volume = $this->default;
819
			$cwd    = $volume->dir($volume->defaultPath());
820
		}
821
		
822
		if (!$cwd) {
823
			return array('error' => $this->error(self::ERROR_OPEN, $hash, self::ERROR_DIR_NOT_FOUND));
824
		}
825
		if (!$cwd['read']) {
826
			return array('error' => $this->error(self::ERROR_OPEN, $hash, self::ERROR_PERM_DENIED));
827
		}
828
829
		$files = array();
830
831
		// get folders trees
832
		if ($tree) {
833
			foreach ($this->volumes as $id => $v) {
834
				$files[] = $v->file($v->root());
835
			}
836
		}
837
838
		// get current working directory files list and add to $files if not exists in it
839
		if (($ls = $volume->scandir($cwd['hash'])) === false) {
840
			return array('error' => $this->error(self::ERROR_OPEN, $cwd['name'], $volume->error()));
841
		}
842
		
843
		if ($ls) {
844
			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...
845
				$files = array_merge($files, $ls);
846
				$files = array_unique($files, SORT_REGULAR);
847
			} else {
848
				$files = $ls;
849
			}
850
		}
851
		
852
		$result = array(
853
			'cwd'     => $cwd,
854
			'options' => $volume->options($cwd['hash']),
855
			'files'   => $files
856
		);
857
858
		if (!empty($args['init'])) {
859
			$result['api'] = $this->version;
860
			$result['uplMaxSize'] = ini_get('upload_max_filesize');
861
			$result['uplMaxFile'] = ini_get('max_file_uploads');
862
			$result['netDrivers'] = array_keys(self::$netDrivers);
863
			if ($volume) {
864
				$result['cwd']['root'] = $volume->root();
865
			}
866
		}
867
		
868
		return $result;
869
	}
870
	
871
	/**
872
	 * Return dir files names list
873
	 *
874
	 * @param  array  command arguments
875
	 * @return array
876
	 * @author Dmitry (dio) Levashov
877
	 **/
878 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...
879
		$target = $args['target'];
880
		
881
		if (($volume = $this->volume($target)) == false
882
		|| ($list = $volume->ls($target)) === false) {
883
			return array('error' => $this->error(self::ERROR_OPEN, '#'.$target));
884
		}
885
		return array('list' => $list);
886
	}
887
	
888
	/**
889
	 * Return subdirs for required directory
890
	 *
891
	 * @param  array  command arguments
892
	 * @return array
893
	 * @author Dmitry (dio) Levashov
894
	 **/
895 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...
896
		$target = $args['target'];
897
		
898
		if (($volume = $this->volume($target)) == false
899
		|| ($tree = $volume->tree($target)) == false) {
900
			return array('error' => $this->error(self::ERROR_OPEN, '#'.$target));
901
		}
902
903
		return array('tree' => $tree);
904
	}
905
	
906
	/**
907
	 * Return parents dir for required directory
908
	 *
909
	 * @param  array  command arguments
910
	 * @return array
911
	 * @author Dmitry (dio) Levashov
912
	 **/
913 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...
914
		$target = $args['target'];
915
		
916
		if (($volume = $this->volume($target)) == false
917
		|| ($tree = $volume->parents($target)) == false) {
918
			return array('error' => $this->error(self::ERROR_OPEN, '#'.$target));
919
		}
920
921
		return array('tree' => $tree);
922
	}
923
	
924
	/**
925
	 * Return new created thumbnails list
926
	 *
927
	 * @param  array  command arguments
928
	 * @return array
929
	 * @author Dmitry (dio) Levashov
930
	 **/
931
	protected function tmb($args) {
932
		
933
		$result  = array('images' => array());
934
		$targets = $args['targets'];
935
		
936
		foreach ($targets as $target) {
937
			if (($volume = $this->volume($target)) != false
938
			&& (($tmb = $volume->tmb($target)) != false)) {
939
				$result['images'][$target] = $tmb;
940
			}
941
		}
942
		return $result;
943
	}
944
	
945
	/**
946
	 * Required to output file in browser when volume URL is not set 
947
	 * Return array contains opened file pointer, root itself and required headers
948
	 *
949
	 * @param  array  command arguments
950
	 * @return array
951
	 * @author Dmitry (dio) Levashov
952
	 **/
953
	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...
954
		$target   = $args['target'];
955
		$download = !empty($args['download']);
956
		$h403     = 'HTTP/1.x 403 Access Denied';
957
		$h404     = 'HTTP/1.x 404 Not Found';
958
959 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...
960
			return array('error' => 'File not found', 'header' => $h404, 'raw' => true);
961
		}
962
		
963 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...
964
			return array('error' => 'File not found', 'header' => $h404, 'raw' => true);
965
		}
966
		
967
		if (!$file['read']) {
968
			return array('error' => 'Access denied', 'header' => $h403, 'raw' => true);
969
		}
970
		
971 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...
972
			return array('error' => 'File not found', 'header' => $h404, 'raw' => true);
973
		}
974
975
		// allow change MIME type by 'file.pre' callback functions
976
		$mime = isset($args['mime'])? $args['mime'] : $file['mime'];
977
		if ($download) {
978
			$disp = 'attachment';
979
		} else {
980
			$dispInlineRegex = $volume->getOption('dispInlineRegex');
981
			$inlineRegex = false;
982
			if ($dispInlineRegex) {
983
				$inlineRegex = '#' . str_replace('#', '\\#', $dispInlineRegex) . '#';
984
				try {
985
					preg_match($inlineRegex, '');
986
				} catch(Exception $e) {
987
					$inlineRegex = false;
988
				}
989
			}
990
			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...
991
				$inlineRegex = '#^(?:(?:image|text)|application/x-shockwave-flash$)#';
992
			}
993
			$disp  = preg_match($inlineRegex, $mime)? 'inline' : 'attachment';
994
		}
995
		
996
		$filenameEncoded = rawurlencode($file['name']);
997
		if (strpos($filenameEncoded, '%') === false) { // ASCII only
998
			$filename = 'filename="'.$file['name'].'"';
999
		} else {
1000
			$ua = $_SERVER['HTTP_USER_AGENT'];
1001
			if (preg_match('/MSIE [4-8]/', $ua)) { // IE < 9 do not support RFC 6266 (RFC 2231/RFC 5987)
1002
				$filename = 'filename="'.$filenameEncoded.'"';
1003
			} elseif (strpos($ua, 'Chrome') === false && strpos($ua, 'Safari') !== false && preg_match('#Version/[3-5]#', $ua)) { // Safari < 6
1004
				$filename = 'filename="'.str_replace('"', '', $file['name']).'"';
1005
			} 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...
1006
				$filename = 'filename*=UTF-8\'\''.$filenameEncoded;
1007
			}
1008
		}
1009
		
1010
		$result = array(
1011
			'volume'  => $volume,
1012
			'pointer' => $fp,
1013
			'info'    => $file,
1014
			'header'  => array(
1015
				'Content-Type: '.$mime, 
1016
				'Content-Disposition: '.$disp.'; '.$filename,
1017
				'Content-Transfer-Encoding: binary',
1018
				'Content-Length: '.$file['size'],
1019
				'Connection: close'
1020
			)
1021
		);
1022
		if (isset($file['url']) && $file['url'] && $file['url'] != 1) {
1023
			$result['header'][] = 'Content-Location: '.$file['url'];
1024
		}
1025
		return $result;
1026
	}
1027
	
1028
	/**
1029
	 * Count total files size
1030
	 *
1031
	 * @param  array  command arguments
1032
	 * @return array
1033
	 * @author Dmitry (dio) Levashov
1034
	 **/
1035
	protected function size($args) {
1036
		$size = 0;
1037
		
1038
		foreach ($args['targets'] as $target) {
1039
			if (($volume = $this->volume($target)) == false
1040
			|| ($file = $volume->file($target)) == false
1041
			|| !$file['read']) {
1042
				return array('error' => $this->error(self::ERROR_OPEN, '#'.$target));
1043
			}
1044
			
1045
			$size += $volume->size($target);
1046
		}
1047
		return array('size' => $size);
1048
	}
1049
	
1050
	/**
1051
	 * Create directory
1052
	 *
1053
	 * @param  array  command arguments
1054
	 * @return array
1055
	 * @author Dmitry (dio) Levashov
1056
	 **/
1057 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...
1058
		$target = $args['target'];
1059
		$name   = $args['name'];
1060
		
1061
		if (($volume = $this->volume($target)) == false) {
1062
			return array('error' => $this->error(self::ERROR_MKDIR, $name, self::ERROR_TRGDIR_NOT_FOUND, '#'.$target));
1063
		}
1064
1065
		return ($dir = $volume->mkdir($target, $name)) == false
1066
			? array('error' => $this->error(self::ERROR_MKDIR, $name, $volume->error()))
1067
			: array('added' => array($dir));
1068
	}
1069
	
1070
	/**
1071
	 * Create empty file
1072
	 *
1073
	 * @param  array  command arguments
1074
	 * @return array
1075
	 * @author Dmitry (dio) Levashov
1076
	 **/
1077 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...
1078
		$target = $args['target'];
1079
		$name   = $args['name'];
1080
		
1081
		if (($volume = $this->volume($target)) == false) {
1082
			return array('error' => $this->error(self::ERROR_MKFILE, $name, self::ERROR_TRGDIR_NOT_FOUND, '#'.$target));
1083
		}
1084
1085
		return ($file = $volume->mkfile($target, $args['name'])) == false
1086
			? array('error' => $this->error(self::ERROR_MKFILE, $name, $volume->error()))
1087
			: array('added' => array($file));
1088
	}
1089
	
1090
	/**
1091
	 * Rename file
1092
	 *
1093
	 * @param  array  $args
1094
	 * @return array
1095
	 * @author Dmitry (dio) Levashov
1096
	 **/
1097
	protected function rename($args) {
1098
		$target = $args['target'];
1099
		$name   = $args['name'];
1100
		
1101 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...
1102
		||  ($rm  = $volume->file($target)) == false) {
1103
			return array('error' => $this->error(self::ERROR_RENAME, '#'.$target, self::ERROR_FILE_NOT_FOUND));
1104
		}
1105
		$rm['realpath'] = $volume->realpath($target);
1106
		
1107
		return ($file = $volume->rename($target, $name)) == false
1108
			? array('error' => $this->error(self::ERROR_RENAME, $rm['name'], $volume->error()))
1109
			: array('added' => array($file), 'removed' => array($rm));
1110
	}
1111
	
1112
	/**
1113
	 * Duplicate file - create copy with "copy %d" suffix
1114
	 *
1115
	 * @param array  $args  command arguments
1116
	 * @return array
1117
	 * @author Dmitry (dio) Levashov
1118
	 **/
1119
	protected function duplicate($args) {
1120
		$targets = is_array($args['targets']) ? $args['targets'] : array();
1121
		$result  = array('added' => array());
1122
		$suffix  = empty($args['suffix']) ? 'copy' : $args['suffix'];
1123
		
1124
		foreach ($targets as $target) {
1125 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...
1126
			|| ($src = $volume->file($target)) == false) {
1127
				$result['warning'] = $this->error(self::ERROR_COPY, '#'.$target, self::ERROR_FILE_NOT_FOUND);
1128
				break;
1129
			}
1130
			
1131
			if (($file = $volume->duplicate($target, $suffix)) == false) {
1132
				$result['warning'] = $this->error($volume->error());
1133
				break;
1134
			}
1135
			
1136
			$result['added'][] = $file;
1137
		}
1138
		
1139
		return $result;
1140
	}
1141
		
1142
	/**
1143
	 * Remove dirs/files
1144
	 *
1145
	 * @param array  command arguments
1146
	 * @return array
1147
	 * @author Dmitry (dio) Levashov
1148
	 **/
1149
	protected function rm($args) {
1150
		$targets = is_array($args['targets']) ? $args['targets'] : array();
1151
		$result  = array('removed' => array());
1152
		
1153
		foreach ($targets as $target) {
1154 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...
1155
				$result['warning'] = $this->error(self::ERROR_RM, '#'.$target, self::ERROR_FILE_NOT_FOUND);
1156
				return $result;
1157
			}
1158
			if (!$volume->rm($target)) {
1159
				$result['warning'] = $this->error($volume->error());
1160
				return $result;
1161
			}
1162
		}
1163
1164
		return $result;
1165
	}
1166
1167
	/**
1168
	* Get remote contents
1169
	*
1170
	* @param  string   $url     target url
1171
	* @param  int      $timeout timeout (sec)
1172
	* @param  int      $redirect_max redirect max count
1173
	* @param  string   $ua
1174
	* @param  resource $fp
1175
	* @return string or bool(false)
1176
	* @retval string contents
1177
	* @retval false  error
1178
	* @author Naoki Sawada
1179
	**/
1180
	protected function get_remote_contents( &$url, $timeout = 30, $redirect_max = 5, $ua = 'Mozilla/5.0', $fp = null ) {
1181
		$method = (function_exists('curl_exec') && !ini_get('safe_mode'))? 'curl_get_contents' : 'fsock_get_contents'; 
1182
		return $this->$method( $url, $timeout, $redirect_max, $ua, $fp );
1183
	}
1184
	
1185
	/**
1186
	 * Get remote contents with cURL
1187
	 *
1188
	 * @param  string   $url     target url
1189
	 * @param  int      $timeout timeout (sec)
1190
	 * @param  int      $redirect_max redirect max count
1191
	 * @param  string   $ua
1192
	 * @param  resource $outfp
1193
	 * @return string or bool(false)
1194
	 * @retval string contents
1195
	 * @retval false  error
1196
	 * @author Naoki Sawada
1197
	 **/
1198
	 protected function curl_get_contents( &$url, $timeout, $redirect_max, $ua, $outfp ){
1199
		$ch = curl_init();
1200
		curl_setopt( $ch, CURLOPT_URL, $url );
1201
		curl_setopt( $ch, CURLOPT_HEADER, false );
1202
		if ($outfp) {
1203
			curl_setopt( $ch, CURLOPT_FILE, $outfp );
1204
		} else {
1205
			curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
1206
			curl_setopt( $ch, CURLOPT_BINARYTRANSFER, true );
1207
		}
1208
		curl_setopt( $ch, CURLOPT_LOW_SPEED_LIMIT, 1 );
1209
		curl_setopt( $ch, CURLOPT_LOW_SPEED_TIME, $timeout );
1210
		curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false );
1211
		curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, 1);
1212
		curl_setopt( $ch, CURLOPT_MAXREDIRS, $redirect_max);
1213
		curl_setopt( $ch, CURLOPT_USERAGENT, $ua);
1214
		$result = curl_exec( $ch );
1215
		$url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
1216
		curl_close( $ch );
1217
		return $outfp? $outfp : $result;
1218
	}
1219
	
1220
	/**
1221
	 * Get remote contents with fsockopen()
1222
	 *
1223
	 * @param  string   $url          url
1224
	 * @param  int      $timeout      timeout (sec)
1225
	 * @param  int      $redirect_max redirect max count
1226
	 * @param  string   $ua
1227
	 * @param  resource $outfp
1228
	 * @return string or bool(false)
1229
	 * @retval string contents
1230
	 * @retval false  error
1231
	 * @author Naoki Sawada
1232
	 */
1233
	protected function fsock_get_contents( &$url, $timeout, $redirect_max, $ua, $outfp ) {
1234
1235
		$connect_timeout = 3;
1236
		$connect_try = 3;
1237
		$method = 'GET';
1238
		$readsize = 4096;
1239
1240
		$getSize = null;
1241
		$headers = '';
1242
		
1243
		$arr = parse_url($url);
1244
		if (!$arr){
1245
			// Bad request
1246
			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...
1247
		}
1248
		
1249
		// query
1250
		$arr['query'] = isset($arr['query']) ? '?'.$arr['query'] : '';
1251
		// port
1252
		$arr['port'] = isset($arr['port']) ? $arr['port'] : (!empty($arr['https'])? 443 : 80);
1253
		
1254
		$url_base = $arr['scheme'].'://'.$arr['host'].':'.$arr['port'];
1255
		$url_path = isset($arr['path']) ? $arr['path'] : '/';
1256
		$uri = $url_path.$arr['query'];
1257
		
1258
		$query = $method.' '.$uri." HTTP/1.0\r\n";
1259
		$query .= "Host: ".$arr['host']."\r\n";
1260
		if (!empty($ua)) $query .= "User-Agent: ".$ua."\r\n";
1261
		if (!is_null($getSize)) $query .= 'Range: bytes=0-' . ($getSize - 1) . "\r\n";
1262
		
1263
		$query .= $headers;
1264
1265
		$query .= "\r\n";
1266
1267
		$fp = $connect_try_count = 0;
1268
		while( !$fp && $connect_try_count < $connect_try ) {
1269
	
1270
			$errno = 0;
1271
			$errstr = "";
1272
			$fp = @ fsockopen(
1273
			$arr['https'].$arr['host'],
1274
			$arr['port'],
1275
			$errno,$errstr,$connect_timeout);
1276
			if ($fp) break;
1277
			$connect_try_count++;
1278
			if (connection_aborted()) {
1279
				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...
1280
			}
1281
			sleep(1); // wait 1sec
1282
		}
1283
		
1284
		$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...
1285
		for ($written = 0; $written < strlen($query); $written += $fwrite) {
1286
			$fwrite = fwrite($fp, substr($query, $written));
1287
			if (!$fwrite) {
1288
				break;
1289
			}
1290
		}
1291
		
1292
		$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...
1293
		
1294
		if ($timeout) {
1295
			socket_set_timeout($fp, $timeout);
1296
		}
1297
		
1298
		$_response = '';
1299
		$header = '';
1300
		while($_response !== "\r\n"){
1301
			$_response = fgets($fp, $readsize);
1302
			$header .= $_response;
1303
		};
1304
		
1305
		$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...
1306
		$rc = (int)$rccd[1];
1307
		
1308
		// Redirect
1309
		switch ($rc) {
1310
			case 307: // Temporary Redirect
1311
			case 303: // See Other
1312
			case 302: // Moved Temporarily
1313
			case 301: // Moved Permanently
1314
				$matches = array();
1315
				if (preg_match('/^Location: (.+?)(#.+)?$/im',$header,$matches) && --$redirect_max > 0) {
1316
					$url = trim($matches[1]);
1317
					$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...
1318
					if (!preg_match('/^https?:\//',$url)) { // no scheme
1319
						if ($url{0} != '/') { // Relative path
1320
							// to Absolute path
1321
							$url = substr($url_path,0,strrpos($url_path,'/')).'/'.$url;
1322
						}
1323
						// add sheme,host
1324
						$url = $url_base.$url;
1325
					}
1326
					fclose($fp);
1327
					return $this->fsock_get_contents( $url, $timeout, $redirect_max, $ua, $outfp );
1328
				}
1329
		}
1330
		
1331
		$body = '';
1332
		if (!$outfp) {
1333
			$outfp = fopen('php://temp', 'rwb');
1334
			$body = true;
1335
		}
1336
		while(fwrite($outfp, fread($fp, $readsize))) {
1337
			if ($timeout) {
1338
				$_status = socket_get_status($fp);
1339
				if ($_status['timed_out']) {
1340
					fclose($outfp);
1341
					fclose($fp);
1342
					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...
1343
				}
1344
			}
1345
		}
1346
		if ($body) {
1347
			rewind($outfp);
1348
			$body = stream_get_contents($outfp);
1349
			fclose($outfp);
1350
			$outfp = null;
1351
		}
1352
		
1353
		fclose($fp);
1354
		
1355
		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 1355 which is incompatible with the return type documented by elFinder::fsock_get_contents of type string.
Loading history...
1356
	}
1357
	
1358
	/**
1359
	 * Parse Data URI scheme
1360
	 * 
1361
	 * @param  string $str
1362
	 * @param  array  $extTable
1363
	 * @return array
1364
	 * @author Naoki Sawada
1365
	 */
1366
	protected function parse_data_scheme( $str, $extTable ) {
1367
		$data = $name = '';
1368
		if ($fp = fopen('data://'.substr($str, 5), 'rb')) {
1369 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...
1370
				$meta = stream_get_meta_data($fp);
1371
				$ext = isset($extTable[$meta['mediatype']])? '.' . $extTable[$meta['mediatype']] : '';
1372
				$name = substr(md5($data), 0, 8) . $ext;
1373
			}
1374
			fclose($fp);
1375
		}
1376
		return array($data, $name);
1377
	}
1378
	
1379
	/**
1380
	 * Detect file type extension by local path
1381
	 * 
1382
	 * @param  string $path Local path
1383
	 * @return string file type extension with dot
1384
	 * @author Naoki Sawada
1385
	 */
1386
	protected function detectFileExtension($path) {
1387
		static $type, $finfo, $extTable;
1388
		if (!$type) {
1389
			$keys = array_keys($this->volumes);
1390
			$volume = $this->volumes[$keys[0]];
1391
			$extTable = array_flip(array_unique($volume->getMimeTable()));
1392
			
1393
			if (class_exists('finfo', false)) {
1394
				$tmpFileInfo = @explode(';', @finfo_file(finfo_open(FILEINFO_MIME), __FILE__));
1395
			} else {
1396
				$tmpFileInfo = false;
1397
			}
1398
			$regexp = '/text\/x\-(php|c\+\+)/';
1399
			if ($tmpFileInfo && preg_match($regexp, array_shift($tmpFileInfo))) {
1400
				$type = 'finfo';
1401
				$finfo = finfo_open(FILEINFO_MIME);
1402
			} elseif (function_exists('mime_content_type')
1403
					&& 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...
1404
				$type = 'mime_content_type';
1405
			} elseif (function_exists('getimagesize')) {
1406
				$type = 'getimagesize';
1407
			} else {
1408
				$type = 'none';
1409
			}
1410
		}
1411
		
1412
		$mime = '';
1413
		if ($type === 'finfo') {
1414
			$mime = @finfo_file($finfo, $path);
1415
		} elseif ($type === 'mime_content_type') {
1416
			$mime = mime_content_type($path);
1417
		} elseif ($type === 'getimagesize') {
1418
			if ($img = @getimagesize($path)) {
1419
				$mime = $img['mime'];
1420
			}
1421
		}
1422
		
1423
		if ($mime) {
1424
			$mime = explode(';', $mime);
1425
			$mime = trim($mime[0]);
1426
			
1427 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...
1428
				// finfo return this mime for empty files
1429
				$mime = 'text/plain';
1430
			} elseif ($mime == 'application/x-zip') {
1431
				// http://elrte.org/redmine/issues/163
1432
				$mime = 'application/zip';
1433
			}
1434
		}
1435
		
1436
		return ($mime && isset($extTable[$mime]))? ('.' . $extTable[$mime]) : '';
1437
	}
1438
	
1439
	/**
1440
	 * Check chunked upload files
1441
	 * 
1442
	 * @param string $tmpname  uploaded temporary file path
1443
	 * @param string $chunk    uploaded chunk file name
1444
	 * @param string $cid      uploaded chunked file id
1445
	 * @param string $tempDir  temporary dirctroy path
1446
	 * @return array (string JoinedTemporaryFilePath, string FileName) or (empty, empty)
1447
	 * @author Naoki Sawada
1448
	 */
1449
	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...
1450
		if (preg_match('/^(.+)(\.\d+_(\d+))\.part$/s', $chunk, $m)) {
1451
			$fname = $m[1];
1452
			$encname = md5($cid . '_' . $fname);
1453
			$base = $tempDir . DIRECTORY_SEPARATOR . 'ELF' . $encname;
1454
			$clast = intval($m[3]);
1455
			if (is_null($tmpname)) {
1456
				ignore_user_abort(true);
1457
				sleep(10); // wait 10 sec
1458
				// chunked file upload fail
1459
				foreach(glob($base . '*') as $cf) {
1460
					@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...
1461
				}
1462
				ignore_user_abort(false);
1463
				return;
1464
			}
1465
			
1466
			$range = isset($_POST['range'])? trim($_POST['range']) : '';
1467
			if ($range && preg_match('/^(\d+),(\d+),(\d+)$/', $range, $ranges)) {
1468
				$start = $ranges[1];
1469
				$len   = $ranges[2];
1470
				$size  = $ranges[3];
1471
				$tmp = $base . '.part';
1472
				$csize = filesize($tmpname);
1473
				
1474
				$tmpExists = is_file($tmp);
1475
				if (!$tmpExists) {
1476
					// check upload max size
1477
					$uploadMaxSize = $volume->getUploadMaxSize();
1478
					if ($uploadMaxSize > 0 && $size > $uploadMaxSize) {
1479
						return array(self::ERROR_UPLOAD_FILE_SIZE, false);
1480
					}
1481
					// make temp file
1482
					$ok = false;
1483
					if ($fp = fopen($tmp, 'wb')) {
1484
						flock($fp, LOCK_EX);
1485
						$ok = ftruncate($fp, $size);
1486
						flock($fp, LOCK_UN);
1487
						fclose($fp);
1488
						touch($base);
1489
					}
1490
					if (!$ok) {
1491
						return array(self::ERROR_UPLOAD_TEMP, false);
1492
					}
1493
				} else {
1494
					// wait until makeing temp file (for anothor session)
1495
					$cnt = 100; // Time limit 10 sec
1496
					while(!is_file($base) && --$cnt) {
1497
						usleep(100000); // wait 100ms
1498
					}
1499
					if (!$cnt) {
1500
						return array(self::ERROR_UPLOAD_TEMP, false);
1501
					}
1502
				}
1503
				
1504
				// check size info
1505
				if ($len != $csize || $start + $len > $size || ($tmpExists && $size != filesize($tmp))) {
1506
					return array(self::ERROR_UPLOAD_TEMP, false);
1507
				}
1508
				
1509
				// write chunk data
1510
				$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...
1511
				$src = fopen($tmpname, 'rb');
1512
				$fp = fopen($tmp, 'cb');
1513
				fseek($fp, $start);
1514
				$writelen = stream_copy_to_stream($src, $fp, $len);
1515
				fclose($fp);
1516
				fclose($src);
1517
				if ($writelen != $len) {
1518
					return array(self::ERROR_UPLOAD_TEMP, false);
1519
				}
1520
				
1521
				// write counts
1522
				file_put_contents($base, "\0", FILE_APPEND);
1523
				
1524
				if (filesize($base) >= $clast + 1) {
1525
					// Completion
1526
					unlink($base);
1527
					return array($tmp, $fname);
1528
				}
1529
			} else {
1530
				// old way
1531
				$part = $base . $m[2];
1532
				if (move_uploaded_file($tmpname, $part)) {
1533
					@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...
1534
					if ($clast < count(glob($base . '*'))) {
1535
						$parts = array();
1536
						for ($i = 0; $i <= $clast; $i++) {
1537
							$name = $base . '.' . $i . '_' . $clast;
1538
							if (is_readable($name)) {
1539
								$parts[] = $name;
1540
							} else {
1541
								$parts = null;
1542
								break;
1543
							}
1544
						}
1545
						if ($parts) {
1546
							if (!is_file($base)) {
1547
								touch($base);
1548
								if ($resfile = tempnam($tempDir, 'ELF')) {
1549
									$target = fopen($resfile, 'wb');
1550
									foreach($parts as $f) {
1551
										$fp = fopen($f, 'rb');
1552
										while (!feof($fp)) {
1553
											fwrite($target, fread($fp, 8192));
1554
										}
1555
										fclose($fp);
1556
										unlink($f);
1557
									}
1558
									fclose($target);
1559
									unlink($base);
1560
									return array($resfile, $fname);
1561
								}
1562
								unlink($base);
1563
							}
1564
						}
1565
					}
1566
				}
1567
			}
1568
		}
1569
		return array('', '');
1570
	}
1571
	
1572
	/**
1573
	 * Get temporary dirctroy path
1574
	 * 
1575
	 * @param  string $volumeTempPath
1576
	 * @return string
1577
	 * @author Naoki Sawada
1578
	 */
1579
	private function getTempDir($volumeTempPath = null) {
1580
		$testDirs = array();
1581
		if ($this->uploadTempPath) {
1582
			$testDirs[] = rtrim(realpath($this->uploadTempPath), DIRECTORY_SEPARATOR);
1583
		}
1584
		if (function_exists('sys_get_temp_dir')) {
1585
			$testDirs[] = sys_get_temp_dir();
1586
		}
1587
		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...
1588
			$testDirs[] = rtrim(realpath($volumeTempPath), DIRECTORY_SEPARATOR);
1589
		}
1590
		$tempDir = '';
1591
		$test = DIRECTORY_SEPARATOR . microtime(true);
1592
		foreach($testDirs as $testDir) {
1593
			if (!$testDir || !is_dir($testDir)) continue;
1594
			$testFile = $testDir.$test;
1595
			if (touch($testFile)) {
1596
				unlink($testFile);
1597
				$tempDir = $testDir;
1598
				$gc = time() - 3600;
1599
				foreach(glob($tempDir . '/ELF*') as $cf) {
1600
					if (filemtime($cf) < $gc) {
1601
						@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...
1602
					}
1603
				}
1604
				break;
1605
			}
1606
		}
1607
		return $tempDir;
1608
	}
1609
	
1610
	/**
1611
	 * chmod
1612
	 *
1613
	 * @param array  command arguments
1614
	 * @return array
1615
	 * @author David Bartle
1616
	 **/
1617
	protected function chmod($args) {
1618
		$targets = $args['targets'];
1619
		$mode    = intval((string)$args['mode'], 8);
1620
1621
		if (!is_array($targets)) {
1622
			$targets = array($targets);
1623
		}
1624
		
1625
		$result = array();
1626
		
1627 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...
1628
			$result['error'] = $this->error(self::ERROR_CONF_NO_VOL);
1629
			return $result;
1630
		}
1631
1632
		$files = array();
1633
		$errors = array();
1634
		foreach($targets as $target) {
1635
			$file = $volume->chmod($target, $mode);
1636
			if ($file) {
1637
				$files = array_merge($files, is_array($file)? $file : array($file));
1638
			} else {
1639
				$errors = array_merge($errors, $volume->error());
1640
			}
1641
		}
1642
		
1643
		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...
1644
			$result['changed'] = $files;
1645
			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...
1646
				$result['warning'] = $this->error($errors);
1647
			}
1648
		} else {
1649
			$result['error'] = $this->error($errors);
1650
		}
1651
		
1652
		return $result;
1653
	}
1654
	
1655
	/**
1656
	 * Save uploaded files
1657
	 *
1658
	 * @param  array
1659
	 * @return array
1660
	 * @author Dmitry (dio) Levashov
1661
	 **/
1662
	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...
1663
		$ngReg  = '/[\/\\?*:|"<>]/';
1664
		$target = $args['target'];
1665
		$volume = $this->volume($target);
1666
		$files  = isset($args['FILES']['upload']) && is_array($args['FILES']['upload']) ? $args['FILES']['upload'] : array();
1667
		$header = empty($args['html']) ? array() : array('header' => 'Content-Type: text/html; charset=utf-8');
1668
		$result = array_merge(array('added' => array()), $header);
1669
		$paths  = $args['upload_path']? $args['upload_path'] : array();
1670
		$chunk  = $args['chunk']? $args['chunk'] : '';
1671
		$cid    = $args['cid']? (int)$args['cid'] : '';
1672
		
1673
		$renames= array();
1674
		$suffix = '~';
1675
		if ($args['renames'] && is_array($args['renames'])) {
1676
			$renames = array_flip($args['renames']);
1677 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...
1678
				$suffix = $args['suffix'];
1679
			}
1680
		}
1681
		
1682
		if (!$volume) {
1683
			return array_merge(array('error' => $this->error(self::ERROR_UPLOAD, self::ERROR_TRGDIR_NOT_FOUND, '#'.$target)), $header);
1684
		}
1685
		
1686
		// regist Shutdown function
1687
		$GLOBALS['elFinderTempFiles'] = array();
1688
// 		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...
1689
// 			$shutdownfunc = function(){ // <- Parse error on PHP < 5.3 ;-(
1690
// 				foreach(array_keys($GLOBALS['elFinderTempFiles']) as $f){
1691
// 					@unlink($f);
1692
// 				}
1693
// 			};
1694
// 		} else {
1695
			$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...
1696
				foreach(array_keys($GLOBALS[\'elFinderTempFiles\']) as $f){
1697
					@unlink($f);
1698
				}
1699
			');
1700
//		}
1701
		register_shutdown_function($shutdownfunc);
1702
		
1703
		// file extentions table by MIME
1704
		$extTable = array_flip(array_unique($volume->getMimeTable()));
1705
		
1706
		if (empty($files)) {
1707
			if (!$args['upload'] && $args['name'] && is_array($args['name'])) {
1708
				$error = '';
1709
				$result['name'] = array();
1710
				foreach($args['name'] as $_i => $_name) {
1711
					if (!$volume->isUploadableByName($_name)) {
1712
						$error = $this->error(self::ERROR_UPLOAD_FILE, $_name, self::ERROR_UPLOAD_FILE_MIME);
1713
						break;
1714
					}
1715
					$result['name'][$_i] = preg_replace($ngReg, '_', $_name);
1716
				}
1717
				if ($error) {
1718
					$result['error'] = $error;
1719
					return $result;
1720
				}
1721
				$result = array_merge_recursive($result, $this->ls($args));
1722
				if (empty($result['list'])) {
1723
					$result['name'] = array();
1724
				} else {
1725
					$result['name'] = array_merge(array_intersect($result['name'], $result['list']));
1726
				}
1727
				return $result;
1728
			}
1729
			if (isset($args['upload']) && is_array($args['upload']) && ($tempDir = $this->getTempDir($volume->getTempPath()))) {
1730
				$names = array();
1731
				foreach($args['upload'] as $i => $url) {
1732
					// check chunked file upload commit
1733
					if ($args['chunk']) {
1734
						if ($url === 'chunkfail' && $args['mimes'] === 'chunkfail') {
1735
							$this->checkChunkedFile(null, $chunk, $cid, $tempDir);
1736
							if (preg_match('/^(.+)(\.\d+_(\d+))\.part$/s', $chunk, $m)) {
1737
								$result['warning'] = $this->error(self::ERROR_UPLOAD_FILE, $m[1], self::ERROR_UPLOAD_TRANSFER);
1738
							}
1739
							return $result;
1740
						} else {
1741
							$tmpfname = $tempDir . '/' . $args['chunk'];
1742
							$files['tmp_name'][$i] = $tmpfname;
1743
							$files['name'][$i] = $url;
1744
							$files['error'][$i] = 0;
1745
							$GLOBALS['elFinderTempFiles'][$tmpfname] = true;
1746
							break;
1747
						}
1748
					}
1749
					
1750
					$tmpfname = $tempDir . DIRECTORY_SEPARATOR . 'ELF_FATCH_' . md5($url.microtime(true));
1751
					
1752
					$_name = '';
1753
					// check is data:
1754
					if (substr($url, 0, 5) === 'data:') {
1755
						list($data, $args['name'][$i]) = $this->parse_data_scheme($url, $extTable);
1756
					} else {
1757
						$fp = fopen($tmpfname, 'wb');
1758
						$data = $this->get_remote_contents($url, 30, 5, 'Mozilla/5.0', $fp);
1759
						$_POST['overwrite'] = false;
1760
						$_name = preg_replace('~^.*?([^/#?]+)(?:\?.*)?(?:#.*)?$~', '$1', rawurldecode($url));
1761
						// Check `Content-Disposition` response header
1762
						if ($data && ($headers = get_headers($url, true)) && !empty($headers['Content-Disposition'])) {
1763
							if (preg_match('/filename\*?=(?:(.+?)\'\')?"?([a-z0-9_.~%-]+)"?/i', $headers['Content-Disposition'], $m)) {
1764
								$_name = rawurldecode($m[2]);
1765
								if ($m[1] && strtoupper($m[1]) !== 'UTF-8' && function_exists('mb_convert_encoding')) {
1766
									$_name = mb_convert_encoding($_name, 'UTF-8', $m[1]);
1767
								}
1768
							}
1769
						}
1770
					}
1771
					if ($data) {
1772
						if (isset($args['name'][$i])) {
1773
							$_name = $args['name'][$i];
1774
						}
1775
						if ($_name) {
1776
							$_ext = '';
1777
							if (preg_match('/(\.[a-z0-9]{1,7})$/', $_name, $_match)) {
1778
								$_ext = $_match[1];
1779
							}
1780
							if ((is_resource($data) && fclose($data)) || file_put_contents($tmpfname, $data)) {
1781
								$GLOBALS['elFinderTempFiles'][$tmpfname] = true;
1782
								$_name = preg_replace($ngReg, '_', $_name);
1783
								list($_a, $_b) = array_pad(explode('.', $_name, 2), 2, '');
1784
								if ($_b === '') {
1785
									if ($_ext) {
1786
										rename($tmpfname, $tmpfname . $_ext);
1787
										$tmpfname = $tmpfname . $_ext;
1788
									}
1789
									$_b = $this->detectFileExtension($tmpfname);
1790
									$_name = $_a.$_b;
1791
								} else {
1792
									$_b = '.'.$_b;
1793
								}
1794
								if (isset($names[$_name])) {
1795
									$_name = $_a.'_'.$names[$_name]++.$_b;
1796
								} else {
1797
									$names[$_name] = 1;
1798
								}
1799
								$files['tmp_name'][$i] = $tmpfname;
1800
								$files['name'][$i] = $_name;
1801
								$files['error'][$i] = 0;
1802
							} else {
1803
								@ 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...
1804
							}
1805
						}
1806
					}
1807
				}
1808
			}
1809
			if (empty($files)) {
1810
				return array_merge(array('error' => $this->error(self::ERROR_UPLOAD, self::ERROR_UPLOAD_NO_FILES)), $header);
1811
			}
1812
		}
1813
		
1814
		foreach ($files['name'] as $i => $name) {
1815
			if (($error = $files['error'][$i]) > 0) {
1816
				$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);
1817
				$this->uploadDebug = 'Upload error code: '.$error;
1818
				break;
1819
			}
1820
			
1821
			$tmpname = $files['tmp_name'][$i];
1822
			$path = ($paths && !empty($paths[$i]))? $paths[$i] : '';
1823
			if ($name === 'blob') {
1824
				if ($chunk) {
1825
					if ($tempDir = $this->getTempDir($volume->getTempPath())) {
1826
						list($tmpname, $name) = $this->checkChunkedFile($tmpname, $chunk, $cid, $tempDir, $volume);
1827
						if ($tmpname) {
1828
							if ($name === false) {
1829
								preg_match('/^(.+)(\.\d+_(\d+))\.part$/s', $chunk, $m);
1830
								$result['error'] = $this->error(self::ERROR_UPLOAD_FILE, $m[1], $tmpname);
1831
								$result['_chunkfailure'] = true;
1832
								$this->uploadDebug = 'Upload error: ' . $tmpname;
1833
							} else if ($name) {
1834
								$result['_chunkmerged'] = basename($tmpname);
1835
								$result['_name'] = $name;
1836
							}
1837
						}
1838
					} else {
1839
						$result['error'] = $this->error(self::ERROR_UPLOAD_FILE, $chunk, self::ERROR_UPLOAD_TRANSFER);
1840
						$this->uploadDebug = 'Upload error: unable open tmp file';
1841
					}
1842
					return $result;
1843 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...
1844
					// for form clipboard with Google Chrome
1845
					$type = $files['type'][$i];
1846
					$ext = isset($extTable[$type])? '.' . $extTable[$type] : '';
1847
					$name = substr(md5(basename($tmpname)), 0, 8) . $ext;
1848
				}
1849
			}
1850
			
1851
			// 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...
1852
			if (! empty($this->listeners['upload.presave'])) {
1853 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...
1854
					call_user_func_array($handler, array(&$path, &$name, $tmpname, $this, $volume));
1855
				}
1856
			}
1857
			
1858
			if (($fp = fopen($tmpname, 'rb')) == false) {
1859
				$result['warning'] = $this->error(self::ERROR_UPLOAD_FILE, $name, self::ERROR_UPLOAD_TRANSFER);
1860
				$this->uploadDebug = 'Upload error: unable open tmp file';
1861
				if (! is_uploaded_file($tmpname)) {
1862
					if (@ unlink($tmpname)) unset($GLOBALS['elFinderTempFiles'][$tmpfname]);
1863
					continue;
1864
				}
1865
				break;
1866
			}
1867
			$rnres = array();
1868
			if ($path) {
1869
				$_target = $volume->getUploadTaget($target, $path, $result);
1870
			} else {
1871
				$_target = $target;
1872
				// file rename for backup
1873
				if (isset($renames[$name])) {
1874
					$dir = $volume->realpath($_target);
1875
					$hash = $volume->getHash($dir, $name);
1876
					$rnres = $this->rename(array('target' => $hash, 'name' => $volume->uniqueName($dir, $name, $suffix, true, 0)));
1877
					if (!empty($rnres['error'])) {
1878
						$result['warning'] = $rnres['error'];
1879
						break;
1880
					}
1881
				}
1882
			}
1883
			if (! $_target || ($file = $volume->upload($fp, $_target, $name, $tmpname)) === false) {
1884
				$result['warning'] = $this->error(self::ERROR_UPLOAD_FILE, $name, $volume->error());
1885
				fclose($fp);
1886 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...
1887
					if (@ unlink($tmpname)) unset($GLOBALS['elFinderTempFiles'][$tmpname]);;
1888
					continue;
1889
				}
1890
				break;
1891
			}
1892
			
1893
			is_resource($fp) && fclose($fp);
1894
			if (! is_uploaded_file($tmpname)){
1895
				clearstatcache();
1896 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...
1897
					unset($GLOBALS['elFinderTempFiles'][$tmpname]);
1898
				}
1899
			}
1900
			$result['added'][] = $file;
1901
			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...
1902
				$result = array_merge_recursive($result, $rnres);
1903
			}
1904
		}
1905
		if ($GLOBALS['elFinderTempFiles']) {
1906
			foreach(array_keys($GLOBALS['elFinderTempFiles']) as $_temp) {
1907
				@ 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...
1908
			}
1909
		}
1910
		$result['removed'] = $volume->removed();
1911
		
1912
		if (!empty($args['node'])) {
1913
			$result['callback'] = array(
1914
				'node' => $args['node'],
1915
				'bind' => 'upload'
1916
			);
1917
		}
1918
		return $result;
1919
	}
1920
		
1921
	/**
1922
	 * Copy/move files into new destination
1923
	 *
1924
	 * @param  array  command arguments
1925
	 * @return array
1926
	 * @author Dmitry (dio) Levashov
1927
	 **/
1928
	protected function paste($args) {
1929
		$dst     = $args['dst'];
1930
		$targets = is_array($args['targets']) ? $args['targets'] : array();
1931
		$cut     = !empty($args['cut']);
1932
		$error   = $cut ? self::ERROR_MOVE : self::ERROR_COPY;
1933
		$result  = array('added' => array(), 'removed' => array());
1934
		
1935
		if (($dstVolume = $this->volume($dst)) == false) {
1936
			return array('error' => $this->error($error, '#'.$targets[0], self::ERROR_TRGDIR_NOT_FOUND, '#'.$dst));
1937
		}
1938
		
1939
		$renames = array();
1940
		$suffix = '~';
1941
		if (!empty($args['renames'])) {
1942
			$renames = array_flip($args['renames']);
1943 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...
1944
				$suffix = $args['suffix'];
1945
			}
1946
		}
1947
		
1948
		foreach ($targets as $target) {
1949 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...
1950
				$result['warning'] = $this->error($error, '#'.$target, self::ERROR_FILE_NOT_FOUND);
1951
				break;
1952
			}
1953
			
1954
			$rnres = array();
1955
			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...
1956
				$file = $srcVolume->file($target);
1957
				if (isset($renames[$file['name']])) {
1958
					$dir = $dstVolume->realpath($dst);
1959
					$hash = $dstVolume->getHash($dir, $file['name']);
1960
					$rnres = $this->rename(array('target' => $hash, 'name' => $dstVolume->uniqueName($dir, $file['name'], $suffix, true, 0)));
1961
					if (!empty($rnres['error'])) {
1962
						$result['warning'] = $rnres['error'];
1963
						break;
1964
					}
1965
				}
1966
			}
1967
			
1968
			if (($file = $dstVolume->paste($srcVolume, $target, $dst, $cut)) == false) {
1969
				$result['warning'] = $this->error($dstVolume->error());
1970
				break;
1971
			}
1972
			
1973
			$result['added'][] = $file;
1974
			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...
1975
				$result = array_merge_recursive($result, $rnres);
1976
			}
1977
		}
1978
		return $result;
1979
	}
1980
	
1981
	/**
1982
	 * Return file content
1983
	 *
1984
	 * @param  array  $args  command arguments
1985
	 * @return array
1986
	 * @author Dmitry (dio) Levashov
1987
	 **/
1988
	protected function get($args) {
1989
		$target = $args['target'];
1990
		$volume = $this->volume($target);
1991
		
1992
		if (!$volume || ($file = $volume->file($target)) == false) {
1993
			return array('error' => $this->error(self::ERROR_OPEN, '#'.$target, self::ERROR_FILE_NOT_FOUND));
1994
		}
1995
		
1996 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...
1997
			return array('error' => $this->error(self::ERROR_OPEN, $volume->path($target), $volume->error()));
1998
		}
1999
		
2000
		if ($args['conv'] && function_exists('mb_detect_encoding') && function_exists('mb_convert_encoding')) {
2001
			$mime = isset($file['mime'])? $file['mime'] : '';
2002
			if ($mime && strtolower(substr($mime, 0, 4)) === 'text') {
2003
				if ($enc = mb_detect_encoding ( $content , mb_detect_order(), true)) {
2004
					if (strtolower($enc) !== 'utf-8') {
2005
						$content = mb_convert_encoding($content, 'UTF-8', $enc);
2006
					}
2007
				}
2008
			}
2009
		}
2010
		
2011
		$json = json_encode($content);
2012
2013
		if ($json === false || strlen($json) < strlen($content)) {
2014
			if ($args['conv']) {
2015
				return array('error' => $this->error(self::ERROR_CONV_UTF8,self::ERROR_NOT_UTF8_CONTENT, $volume->path($target)));
2016
			} else {
2017
				return array('doconv' => true);
2018
			}
2019
		}
2020
		
2021
		return array('content' => $content);
2022
	}
2023
	
2024
	/**
2025
	 * Save content into text file
2026
	 *
2027
	 * @return array
2028
	 * @author Dmitry (dio) Levashov
2029
	 **/
2030
	protected function put($args) {
2031
		$target = $args['target'];
2032
		
2033 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...
2034
		|| ($file = $volume->file($target)) == false) {
2035
			return array('error' => $this->error(self::ERROR_SAVE, '#'.$target, self::ERROR_FILE_NOT_FOUND));
2036
		}
2037
		
2038 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...
2039
			return array('error' => $this->error(self::ERROR_SAVE, $volume->path($target), $volume->error()));
2040
		}
2041
		
2042
		return array('changed' => array($file));
2043
	}
2044
2045
	/**
2046
	 * Extract files from archive
2047
	 *
2048
	 * @param  array  $args  command arguments
2049
	 * @return array
2050
	 * @author Dmitry (dio) Levashov, 
2051
	 * @author Alexey Sukhotin
2052
	 **/
2053
	protected function extract($args) {
2054
		$target = $args['target'];
2055
		$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...
2056
		$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...
2057
		$makedir = isset($args['makedir'])? (bool)$args['makedir'] : null;
2058
2059 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...
2060
		|| ($file = $volume->file($target)) == false) {
2061
			return array('error' => $this->error(self::ERROR_EXTRACT, '#'.$target, self::ERROR_FILE_NOT_FOUND));
2062
		}  
2063
2064
		return ($file = $volume->extract($target, $makedir))
2065
			? array('added' => isset($file['read'])? array($file) : $file)
2066
			: array('error' => $this->error(self::ERROR_EXTRACT, $volume->path($target), $volume->error()));
2067
	}
2068
	
2069
	/**
2070
	 * Create archive
2071
	 *
2072
	 * @param  array  $args  command arguments
2073
	 * @return array
2074
	 * @author Dmitry (dio) Levashov, 
2075
	 * @author Alexey Sukhotin
2076
	 **/
2077
	protected function archive($args) {
2078
		$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...
2079
		$targets = isset($args['targets']) && is_array($args['targets']) ? $args['targets'] : array();
2080
		$name    = isset($args['name'])? $args['name'] : '';
2081
	
2082 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...
2083
			return $this->error(self::ERROR_ARCHIVE, self::ERROR_TRGDIR_NOT_FOUND);
2084
		}
2085
	
2086
		return ($file = $volume->archive($targets, $args['type'], $name))
2087
			? array('added' => array($file))
2088
			: array('error' => $this->error(self::ERROR_ARCHIVE, $volume->error()));
2089
	}
2090
	
2091
	/**
2092
	 * Search files
2093
	 *
2094
	 * @param  array  $args  command arguments
2095
	 * @return array
2096
	 * @author Dmitry Levashov
2097
	 **/
2098
	protected function search($args) {
2099
		$q      = trim($args['q']);
2100
		$mimes  = !empty($args['mimes']) && is_array($args['mimes']) ? $args['mimes'] : array();
2101
		$target = !empty($args['target'])? $args['target'] : null;
2102
		$result = array();
2103
2104
		if (!is_null($target)) {
2105
			$volume = $this->volume($target);
2106
			$result = $volume->search($q, $mimes, $target);
2107
		} else {
2108
			foreach ($this->volumes as $volume) {
2109
				$result = array_merge($result, $volume->search($q, $mimes));
2110
			}
2111
		}
2112
		
2113
		return array('files' => $result);
2114
	}
2115
	
2116
	/**
2117
	 * Return file info (used by client "places" ui)
2118
	 *
2119
	 * @param  array  $args  command arguments
2120
	 * @return array
2121
	 * @author Dmitry Levashov
2122
	 **/
2123
	protected function info($args) {
2124
		$files = array();
2125
		
2126
		foreach ($args['targets'] as $hash) {
2127
			if (($volume = $this->volume($hash)) != false
2128
			&& ($info = $volume->file($hash)) != false) {
2129
				$files[] = $info;
2130
			}
2131
		}
2132
		
2133
		return array('files' => $files);
2134
	}
2135
	
2136
	/**
2137
	 * Return image dimmensions
2138
	 *
2139
	 * @param  array  $args  command arguments
2140
	 * @return array
2141
	 * @author Dmitry (dio) Levashov
2142
	 **/
2143
	protected function dim($args) {
2144
		$target = $args['target'];
2145
		
2146
		if (($volume = $this->volume($target)) != false) {
2147
			$dim = $volume->dimensions($target);
2148
			return $dim ? array('dim' => $dim) : array();
2149
		}
2150
		return array();
2151
	}
2152
	
2153
	/**
2154
	 * Resize image
2155
	 *
2156
	 * @param  array  command arguments
2157
	 * @return array
2158
	 * @author Dmitry (dio) Levashov
2159
	 * @author Alexey Sukhotin
2160
	 **/
2161
	protected function resize($args) {
2162
		$target = $args['target'];
2163
		$width  = $args['width'];
2164
		$height = $args['height'];
2165
		$x      = (int)$args['x'];
2166
		$y      = (int)$args['y'];
2167
		$mode   = $args['mode'];
2168
		$bg     = null;
2169
		$degree = (int)$args['degree'];
2170
		
2171 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...
2172
		|| ($file = $volume->file($target)) == false) {
2173
			return array('error' => $this->error(self::ERROR_RESIZE, '#'.$target, self::ERROR_FILE_NOT_FOUND));
2174
		}
2175
2176
		return ($file = $volume->resize($target, $width, $height, $x, $y, $mode, $bg, $degree))
2177
			? array('changed' => array($file))
2178
			: array('error' => $this->error(self::ERROR_RESIZE, $volume->path($target), $volume->error()));
2179
	}
2180
	
2181
	/**
2182
	* Return content URL
2183
	*
2184
	* @param  array  $args  command arguments
2185
	* @return array
2186
	* @author Naoki Sawada
2187
	**/
2188
	protected function url($args) {
2189
		$target = $args['target'];
2190
		$options = isset($args['options'])? $args['options'] : array();
2191
		if (($volume = $this->volume($target)) != false) {
2192
			$url = $volume->getContentUrl($target, $options);
2193
			return $url ? array('url' => $url) : array();
2194
		}
2195
		return array();
2196
	}
2197
2198
	/**
2199
	 * Output callback result with JavaScript that control elFinder
2200
	 * or HTTP redirect to callbackWindowURL
2201
	 * 
2202
	 * @param  array  command arguments
2203
	 * @author Naoki Sawada
2204
	 */
2205
	protected function callback($args) {
2206
		$checkReg = '/[^a-zA-Z0-9;._-]/';
2207
		$node = (isset($args['node']) && !preg_match($checkReg, $args['node']))? $args['node'] : '';
2208
		$json = (isset($args['json']) && @json_decode($args['json']))? $args['json'] : '{}';
2209
		$bind  = (isset($args['bind']) && !preg_match($checkReg, $args['bind']))? $args['bind'] : '';
2210
		$done = (!empty($args['done']));
2211
		
2212
		while( ob_get_level() ) {
2213
			if (! ob_end_clean()) {
2214
				break;
2215
			}
2216
		}
2217
		
2218
		if ($done || ! $this->callbackWindowURL) {
2219
			$script = '';
2220
			if ($node) {
2221
				$script .= '
2222
					var w = window.opener || window.parent || window;
2223
					try {
2224
						var elf = w.document.getElementById(\''.$node.'\').elfinder;
2225
						if (elf) {
2226
							var data = '.$json.';
2227
							if (data.error) {
2228
								elf.error(data.error);
2229
							} else {
2230
								data.warning && elf.error(data.warning);
2231
								data.removed && data.removed.length && elf.remove(data);
2232
								data.added   && data.added.length   && elf.add(data);
2233
								data.changed && data.changed.length && elf.change(data);';
2234
				if ($bind) {
2235
					$script .= '
2236
								elf.trigger(\''.$bind.'\', data);';
2237
				}
2238
				$script .= '
2239
								data.sync && elf.sync();
2240
							}
2241
						}
2242
					} catch(e) {
2243
						// for CORS
2244
						w.postMessage && w.postMessage(JSON.stringify({bind:\''.$bind.'\',data:'.$json.'}), \'*\');
2245
					}';
2246
			}
2247
			$script .= 'window.close();';
2248
			
2249
			$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>';
2250
			
2251
			header('Content-Type: text/html; charset=utf-8');
2252
			header('Content-Length: '.strlen($out));
2253
			header('Cache-Control: private');
2254
			header('Pragma: no-cache');
2255
			
2256
			echo $out;
2257
			
2258
		} else {
2259
			$url = $this->callbackWindowURL;
2260
			$url .= ((strpos($url, '?') === false)? '?' : '&')
2261
				 . '&node=' . rawurlencode($node)
2262
				 . (($json !== '{}')? ('&json=' . rawurlencode($json)) : '')
2263
				 . ($bind? ('&bind=' .  rawurlencode($bind)) : '')
2264
				 . '&done=1';
2265
			
2266
			header('Location: ' . $url);
2267
			
2268
		}
2269
		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...
2270
	}
2271
2272
	/***************************************************************************/
2273
	/*                                   utils                                 */
2274
	/***************************************************************************/
2275
	
2276
	/**
2277
	 * Return root - file's owner
2278
	 *
2279
	 * @param  string  file hash
2280
	 * @return elFinderStorageDriver
2281
	 * @author Dmitry (dio) Levashov
2282
	 **/
2283
	protected function volume($hash) {
2284
		foreach ($this->volumes as $id => $v) {
2285
			if (strpos(''.$hash, $id) === 0) {
2286
				return $this->volumes[$id];
2287
			} 
2288
		}
2289
		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...
2290
	}
2291
	
2292
	/**
2293
	 * Return files info array 
2294
	 *
2295
	 * @param  array  $data  one file info or files info
2296
	 * @return array
2297
	 * @author Dmitry (dio) Levashov
2298
	 **/
2299
	protected function toArray($data) {
2300
		return isset($data['hash']) || !is_array($data) ? array($data) : $data;
2301
	}
2302
	
2303
	/**
2304
	 * Return fils hashes list
2305
	 *
2306
	 * @param  array  $files  files info
2307
	 * @return array
2308
	 * @author Dmitry (dio) Levashov
2309
	 **/
2310
	protected function hashes($files) {
2311
		$ret = array();
2312
		foreach ($files as $file) {
2313
			$ret[] = $file['hash'];
2314
		}
2315
		return $ret;
2316
	}
2317
	
2318
	/**
2319
	 * Remove from files list hidden files and files with required mime types
2320
	 *
2321
	 * @param  array  $files  files info
2322
	 * @return array
2323
	 * @author Dmitry (dio) Levashov
2324
	 **/
2325
	protected function filter($files) {
2326
		foreach ($files as $i => $file) {
2327
			if (!empty($file['hidden']) || !$this->default->mimeAccepted($file['mime'])) {
2328
				unset($files[$i]);
2329
			}
2330
		}
2331
		return array_merge($files, array());
2332
	}
2333
	
2334
	protected function utime() {
2335
		$time = explode(" ", microtime());
2336
		return (double)$time[1] + (double)$time[0];
2337
	}
2338
	
2339
	
2340
	/***************************************************************************/
2341
	/*                           static  utils                                 */
2342
	/***************************************************************************/
2343
	
2344
	/**
2345
	 * Return Is Animation Gif
2346
	 * 
2347
	 * @param  string $path server local path of target image
2348
	 * @return bool
2349
	 */
2350
	public static function isAnimationGif($path) {
2351
		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...
2352
		switch ($type) {
2353
			case IMAGETYPE_GIF:
2354
				break;
2355
			default:
2356
				return false;
2357
		}
2358
	
2359
		$imgcnt = 0;
2360
		$fp = fopen($path, 'rb');
2361
		@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...
2362
		$c = @fread($fp,1);
2363
		if (ord($c) != 0x39) {  // GIF89a
2364
			return false;
2365
		}
2366
	
2367
		while (!feof($fp)) {
2368
			do {
2369
				$c = fread($fp, 1);
2370
			} while(ord($c) != 0x21 && !feof($fp));
2371
	
2372
			if (feof($fp)) {
2373
				break;
2374
			}
2375
	
2376
			$c2 = fread($fp,2);
2377
			if (bin2hex($c2) == "f904") {
2378
				$imgcnt++;
2379
			}
2380
	
2381
			if (feof($fp)) {
2382
				break;
2383
			}
2384
		}
2385
	
2386
		if ($imgcnt > 1) {
2387
			return true;
2388
		} else {
2389
			return false;
2390
		}
2391
	}
2392
2393
	/**
2394
	 * Return Is seekable stream resource
2395
	 * 
2396
	 * @param resource $resource
2397
	 * @return bool
2398
	 */
2399
	public static function isSeekableStream($resource) {
2400
		$metadata = stream_get_meta_data($resource);
2401
		return $metadata['seekable'];
2402
	}
2403
2404
	/**
2405
	 * serialize and base64_encode of session data (If needed)
2406
	 * 
2407
	 * @param  mixed $var  target variable
2408
	 * @author Naoki Sawada
2409
	 */
2410
	public static function sessionDataEncode($var) {
2411
		if (self::$base64encodeSessionData) {
2412
			$var = base64_encode(serialize($var));
2413
		}
2414
		return $var;
2415
	}
2416
	
2417
	/**
2418
	 * base64_decode and unserialize of session data  (If needed)
2419
	 * 
2420
	 * @param  mixed $var      target variable
2421
	 * @param  bool  $checkIs  data type for check (array|string|object|int)
2422
	 * @author Naoki Sawada
2423
	 */
2424
	public static function sessionDataDecode(&$var, $checkIs = null) {
2425
		if (self::$base64encodeSessionData) {
2426
			$data = @unserialize(@base64_decode($var));
2427
		} else {
2428
			$data = $var;
2429
		}
2430
		$chk = true;
2431
		if ($checkIs) {
2432
			switch ($checkIs) {
2433
				case 'array':
2434
					$chk = is_array($data);
2435
					break;
2436
				case 'string':
2437
					$chk = is_string($data);
2438
					break;
2439
				case 'object':
2440
					$chk = is_object($data);
2441
					break;
2442
				case 'int':
2443
					$chk = is_int($data);
2444
					break;
2445
			}
2446
		}
2447
		if (!$chk) {
2448
			unset($var);
2449
			return false;
2450
		}
2451
		return $data;
2452
	}
2453
} // END class
2454