Completed
Push — master ( 63f9e6...34eb4c )
by Kenji
10s
created

replacing/libraries/old/3.1.7-Upload.php (25 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 49 and the first side effect is on line 38.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
/**
3
 * CodeIgniter
4
 *
5
 * An open source application development framework for PHP
6
 *
7
 * This content is released under the MIT License (MIT)
8
 *
9
 * Copyright (c) 2014 - 2018, British Columbia Institute of Technology
10
 *
11
 * Permission is hereby granted, free of charge, to any person obtaining a copy
12
 * of this software and associated documentation files (the "Software"), to deal
13
 * in the Software without restriction, including without limitation the rights
14
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
 * copies of the Software, and to permit persons to whom the Software is
16
 * furnished to do so, subject to the following conditions:
17
 *
18
 * The above copyright notice and this permission notice shall be included in
19
 * all copies or substantial portions of the Software.
20
 *
21
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27
 * THE SOFTWARE.
28
 *
29
 * @package	CodeIgniter
30
 * @author	EllisLab Dev Team
31
 * @copyright	Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
32
 * @copyright	Copyright (c) 2014 - 2018, British Columbia Institute of Technology (http://bcit.ca/)
33
 * @license	http://opensource.org/licenses/MIT	MIT License
34
 * @link	https://codeigniter.com
35
 * @since	Version 1.0.0
36
 * @filesource
37
 */
38
defined('BASEPATH') OR exit('No direct script access allowed');
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or 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...
39
40
/**
41
 * File Uploading Class (Modified by ci-phpunit-test)
42
 *
43
 * @package		CodeIgniter
44
 * @subpackage	Libraries
45
 * @category	Uploads
46
 * @author		EllisLab Dev Team
47
 * @link		https://codeigniter.com/user_guide/libraries/file_uploading.html
48
 */
49 View Code Duplication
class CI_Upload {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The type CI_Upload has been defined more than once; this definition is ignored, only the first definition in application/tests/_ci_ph...es/old/3.1.2-Upload.php (L49-1360) is considered.

This check looks for classes that have been defined more than once.

If you can, we would recommend to use standard object-oriented programming techniques. For example, to avoid multiple types, it might make sense to create a common interface, and then multiple, different implementations for that interface.

This also has the side-effect of providing you with better IDE auto-completion, static analysis and also better OPCode caching from PHP.

Loading history...
This class 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...
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...
50
51
	/**
52
	 * Maximum file size
53
	 *
54
	 * @var	int
55
	 */
56
	public $max_size = 0;
57
58
	/**
59
	 * Maximum image width
60
	 *
61
	 * @var	int
62
	 */
63
	public $max_width = 0;
64
65
	/**
66
	 * Maximum image height
67
	 *
68
	 * @var	int
69
	 */
70
	public $max_height = 0;
71
72
	/**
73
	 * Minimum image width
74
	 *
75
	 * @var	int
76
	 */
77
	public $min_width = 0;
78
79
	/**
80
	 * Minimum image height
81
	 *
82
	 * @var	int
83
	 */
84
	public $min_height = 0;
85
86
	/**
87
	 * Maximum filename length
88
	 *
89
	 * @var	int
90
	 */
91
	public $max_filename = 0;
92
93
	/**
94
	 * Maximum duplicate filename increment ID
95
	 *
96
	 * @var	int
97
	 */
98
	public $max_filename_increment = 100;
99
100
	/**
101
	 * Allowed file types
102
	 *
103
	 * @var	string
104
	 */
105
	public $allowed_types = '';
106
107
	/**
108
	 * Temporary filename
109
	 *
110
	 * @var	string
111
	 */
112
	public $file_temp = '';
113
114
	/**
115
	 * Filename
116
	 *
117
	 * @var	string
118
	 */
119
	public $file_name = '';
120
121
	/**
122
	 * Original filename
123
	 *
124
	 * @var	string
125
	 */
126
	public $orig_name = '';
127
128
	/**
129
	 * File type
130
	 *
131
	 * @var	string
132
	 */
133
	public $file_type = '';
134
135
	/**
136
	 * File size
137
	 *
138
	 * @var	int
139
	 */
140
	public $file_size = NULL;
141
142
	/**
143
	 * Filename extension
144
	 *
145
	 * @var	string
146
	 */
147
	public $file_ext = '';
148
149
	/**
150
	 * Force filename extension to lowercase
151
	 *
152
	 * @var	string
153
	 */
154
	public $file_ext_tolower = FALSE;
155
156
	/**
157
	 * Upload path
158
	 *
159
	 * @var	string
160
	 */
161
	public $upload_path = '';
162
163
	/**
164
	 * Overwrite flag
165
	 *
166
	 * @var	bool
167
	 */
168
	public $overwrite = FALSE;
169
170
	/**
171
	 * Obfuscate filename flag
172
	 *
173
	 * @var	bool
174
	 */
175
	public $encrypt_name = FALSE;
176
177
	/**
178
	 * Is image flag
179
	 *
180
	 * @var	bool
181
	 */
182
	public $is_image = FALSE;
183
184
	/**
185
	 * Image width
186
	 *
187
	 * @var	int
188
	 */
189
	public $image_width = NULL;
190
191
	/**
192
	 * Image height
193
	 *
194
	 * @var	int
195
	 */
196
	public $image_height = NULL;
197
198
	/**
199
	 * Image type
200
	 *
201
	 * @var	string
202
	 */
203
	public $image_type = '';
204
205
	/**
206
	 * Image size string
207
	 *
208
	 * @var	string
209
	 */
210
	public $image_size_str = '';
211
212
	/**
213
	 * Error messages list
214
	 *
215
	 * @var	array
216
	 */
217
	public $error_msg = array();
218
219
	/**
220
	 * Remove spaces flag
221
	 *
222
	 * @var	bool
223
	 */
224
	public $remove_spaces = TRUE;
225
226
	/**
227
	 * MIME detection flag
228
	 *
229
	 * @var	bool
230
	 */
231
	public $detect_mime = TRUE;
232
233
	/**
234
	 * XSS filter flag
235
	 *
236
	 * @var	bool
237
	 */
238
	public $xss_clean = FALSE;
239
240
	/**
241
	 * Apache mod_mime fix flag
242
	 *
243
	 * @var	bool
244
	 */
245
	public $mod_mime_fix = TRUE;
246
247
	/**
248
	 * Temporary filename prefix
249
	 *
250
	 * @var	string
251
	 */
252
	public $temp_prefix = 'temp_file_';
253
254
	/**
255
	 * Filename sent by the client
256
	 *
257
	 * @var	bool
258
	 */
259
	public $client_name = '';
260
261
	// --------------------------------------------------------------------
262
263
	/**
264
	 * Filename override
265
	 *
266
	 * @var	string
267
	 */
268
	protected $_file_name_override = '';
269
270
	/**
271
	 * MIME types list
272
	 *
273
	 * @var	array
274
	 */
275
	protected $_mimes = array();
276
277
	/**
278
	 * CI Singleton
279
	 *
280
	 * @var	object
281
	 */
282
	protected $_CI;
283
284
	// --------------------------------------------------------------------
285
286
	/**
287
	 * Constructor
288
	 *
289
	 * @param	array	$config
290
	 * @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...
291
	 *
292
	 * @codeCoverageIgnore
293
	 */
294
	public function __construct($config = array())
295
	{
296
		empty($config) OR $this->initialize($config, FALSE);
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or 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...
297
298
		$this->_mimes =& get_mimes();
299
		$this->_CI =& get_instance();
300
301
		log_message('info', 'Upload Class Initialized');
302
	}
303
304
	// --------------------------------------------------------------------
305
306
	/**
307
	 * Initialize preferences
308
	 *
309
	 * @param	array	$config
310
	 * @param	bool	$reset
311
	 * @return	CI_Upload
312
	 *
313
	 * @codeCoverageIgnore
314
	 */
315
	public function initialize(array $config = array(), $reset = TRUE)
316
	{
317
		$reflection = new ReflectionClass($this);
318
319
		if ($reset === TRUE)
320
		{
321
			$defaults = $reflection->getDefaultProperties();
322
			foreach (array_keys($defaults) as $key)
323
			{
324
				if ($key[0] === '_')
325
				{
326
					continue;
327
				}
328
329
				if (isset($config[$key]))
330
				{
331
					if ($reflection->hasMethod('set_'.$key))
332
					{
333
						$this->{'set_'.$key}($config[$key]);
334
					}
335
					else
336
					{
337
						$this->$key = $config[$key];
338
					}
339
				}
340
				else
341
				{
342
					$this->$key = $defaults[$key];
343
				}
344
			}
345
		}
346
		else
347
		{
348
			foreach ($config as $key => &$value)
349
			{
350
				if ($key[0] !== '_' && $reflection->hasProperty($key))
351
				{
352
					if ($reflection->hasMethod('set_'.$key))
353
					{
354
						$this->{'set_'.$key}($value);
355
					}
356
					else
357
					{
358
						$this->$key = $value;
359
					}
360
				}
361
			}
362
		}
363
364
		// if a file_name was provided in the config, use it instead of the user input
365
		// supplied file name for all uploads until initialized again
366
		$this->_file_name_override = $this->file_name;
367
		return $this;
368
	}
369
370
	// --------------------------------------------------------------------
371
372
	/**
373
	 * Perform the file upload
374
	 *
375
	 * @param	string	$field
376
	 * @return	bool
377
	 *
378
	 * modified by ci-phpunit-test
379
	 */
380
	public function do_upload($field = 'userfile')
0 ignored issues
show
do_upload uses the super-global variable $_FILES 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...
381
	{
382
		// Is $_FILES[$field] set? If not, no reason to continue.
383
		if (isset($_FILES[$field]))
384
		{
385
			$_file = $_FILES[$field];
386
		}
387
		// Does the field name contain array notation?
388
		elseif (($c = preg_match_all('/(?:^[^\[]+)|\[[^]]*\]/', $field, $matches)) > 1)
389
		{
390
			$_file = $_FILES;
391
			for ($i = 0; $i < $c; $i++)
392
			{
393
				// We can't track numeric iterations, only full field names are accepted
394
				if (($field = trim($matches[0][$i], '[]')) === '' OR ! isset($_file[$field]))
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or 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...
395
				{
396
					$_file = NULL;
397
					break;
398
				}
399
400
				$_file = $_file[$field];
401
			}
402
		}
403
404
		if ( ! isset($_file))
405
		{
406
			$this->set_error('upload_no_file_selected', 'debug');
407
			return FALSE;
408
		}
409
410
		// Is the upload path valid?
411
		if ( ! $this->validate_upload_path())
412
		{
413
			// errors will already be set by validate_upload_path() so just return FALSE
414
			return FALSE;
415
		}
416
417
		// Was the file able to be uploaded? If not, determine the reason why.
418
		if ( ! file_exists($_file['tmp_name']))	// modified by ci-phpunit-test
419
		{
420
			$error = isset($_file['error']) ? $_file['error'] : 4;
421
422
			switch ($error)
423
			{
424
				case UPLOAD_ERR_INI_SIZE:
425
					$this->set_error('upload_file_exceeds_limit', 'info');
426
					break;
427
				case UPLOAD_ERR_FORM_SIZE:
428
					$this->set_error('upload_file_exceeds_form_limit', 'info');
429
					break;
430
				case UPLOAD_ERR_PARTIAL:
431
					$this->set_error('upload_file_partial', 'debug');
432
					break;
433
				case UPLOAD_ERR_NO_FILE:
434
					$this->set_error('upload_no_file_selected', 'debug');
435
					break;
436
				case UPLOAD_ERR_NO_TMP_DIR:
437
					$this->set_error('upload_no_temp_directory', 'error');
438
					break;
439
				case UPLOAD_ERR_CANT_WRITE:
440
					$this->set_error('upload_unable_to_write_file', 'error');
441
					break;
442
				case UPLOAD_ERR_EXTENSION:
443
					$this->set_error('upload_stopped_by_extension', 'debug');
444
					break;
445
				default:
446
					$this->set_error('upload_no_file_selected', 'debug');
447
					break;
448
			}
449
450
			return FALSE;
451
		}
452
453
		// Set the uploaded data as class variables
454
		$this->file_temp = $_file['tmp_name'];
455
		$this->file_size = $_file['size'];
456
457
		// Skip MIME type detection?
458
		if ($this->detect_mime !== FALSE)
459
		{
460
			$this->_file_mime_type($_file);
461
		}
462
463
		$this->file_type = preg_replace('/^(.+?);.*$/', '\\1', $this->file_type);
464
		$this->file_type = strtolower(trim(stripslashes($this->file_type), '"'));
465
		$this->file_name = $this->_prep_filename($_file['name']);
466
		$this->file_ext	 = $this->get_extension($this->file_name);
467
		$this->client_name = $this->file_name;
0 ignored issues
show
Documentation Bug introduced by
The property $client_name was declared of type boolean, but $this->file_name is of type string. 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...
468
469
		// Is the file type allowed to be uploaded?
470
		if ( ! $this->is_allowed_filetype())
471
		{
472
			$this->set_error('upload_invalid_filetype', 'debug');
473
			return FALSE;
474
		}
475
476
		// if we're overriding, let's now make sure the new name and type is allowed
477
		if ($this->_file_name_override !== '')
478
		{
479
			$this->file_name = $this->_prep_filename($this->_file_name_override);
480
481
			// If no extension was provided in the file_name config item, use the uploaded one
482
			if (strpos($this->_file_name_override, '.') === FALSE)
483
			{
484
				$this->file_name .= $this->file_ext;
485
			}
486
			else
487
			{
488
				// An extension was provided, let's have it!
489
				$this->file_ext	= $this->get_extension($this->_file_name_override);
490
			}
491
492
			if ( ! $this->is_allowed_filetype(TRUE))
493
			{
494
				$this->set_error('upload_invalid_filetype', 'debug');
495
				return FALSE;
496
			}
497
		}
498
499
		// Convert the file size to kilobytes
500
		if ($this->file_size > 0)
501
		{
502
			$this->file_size = round($this->file_size/1024, 2);
0 ignored issues
show
Documentation Bug introduced by
The property $file_size was declared of type integer, but round($this->file_size / 1024, 2) 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...
503
		}
504
505
		// Is the file size within the allowed maximum?
506
		if ( ! $this->is_allowed_filesize())
507
		{
508
			$this->set_error('upload_invalid_filesize', 'info');
509
			return FALSE;
510
		}
511
512
		// Are the image dimensions within the allowed size?
513
		// Note: This can fail if the server has an open_basedir restriction.
514
		if ( ! $this->is_allowed_dimensions())
515
		{
516
			$this->set_error('upload_invalid_dimensions', 'info');
517
			return FALSE;
518
		}
519
520
		// Sanitize the file name for security
521
		$this->file_name = $this->_CI->security->sanitize_filename($this->file_name);
522
523
		// Truncate the file name if it's too long
524
		if ($this->max_filename > 0)
525
		{
526
			$this->file_name = $this->limit_filename_length($this->file_name, $this->max_filename);
527
		}
528
529
		// Remove white spaces in the name
530
		if ($this->remove_spaces === TRUE)
531
		{
532
			$this->file_name = preg_replace('/\s+/', '_', $this->file_name);
533
		}
534
535
		if ($this->file_ext_tolower && ($ext_length = strlen($this->file_ext)))
536
		{
537
			// file_ext was previously lower-cased by a get_extension() call
538
			$this->file_name = substr($this->file_name, 0, -$ext_length).$this->file_ext;
539
		}
540
541
		/*
542
		 * Validate the file name
543
		 * This function appends an number onto the end of
544
		 * the file if one with the same name already exists.
545
		 * If it returns false there was a problem.
546
		 */
547
		$this->orig_name = $this->file_name;
548
		if (FALSE === ($this->file_name = $this->set_filename($this->upload_path, $this->file_name)))
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->set_filename($thi...path, $this->file_name) can also be of type false. However, the property $file_name is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
549
		{
550
			return FALSE;
551
		}
552
553
		/*
554
		 * Run the file through the XSS hacking filter
555
		 * This helps prevent malicious code from being
556
		 * embedded within a file. Scripts can easily
557
		 * be disguised as images or other file types.
558
		 */
559
		if ($this->xss_clean && $this->do_xss_clean() === FALSE)
560
		{
561
			$this->set_error('upload_unable_to_write_file', 'error');
562
			return FALSE;
563
		}
564
565
		/*
566
		 * Move the file to the final destination
567
		 * To deal with different server configurations
568
		 * we'll attempt to use copy() first. If that fails
569
		 * we'll use move_uploaded_file(). One of the two should
570
		 * reliably work in most environments
571
		 */
572
		if ( ! @copy($this->file_temp, $this->upload_path.$this->file_name))
573
		{
574
			if ( ! @move_uploaded_file($this->file_temp, $this->upload_path.$this->file_name))
575
			{
576
				$this->set_error('upload_destination_error', 'error');
577
				return FALSE;
578
			}
579
		}
580
581
		/*
582
		 * Set the finalized image dimensions
583
		 * This sets the image width/height (assuming the
584
		 * file was an image). We use this information
585
		 * in the "data" function.
586
		 */
587
		$this->set_image_properties($this->upload_path.$this->file_name);
588
589
		return TRUE;
590
	}
591
592
	// --------------------------------------------------------------------
593
594
	/**
595
	 * Finalized Data Array
596
	 *
597
	 * Returns an associative array containing all of the information
598
	 * related to the upload, allowing the developer easy access in one array.
599
	 *
600
	 * @param	string	$index
601
	 * @return	mixed
602
	 *
603
	 * @codeCoverageIgnore
604
	 */
605
	public function data($index = NULL)
606
	{
607
		$data = array(
608
				'file_name'		=> $this->file_name,
609
				'file_type'		=> $this->file_type,
610
				'file_path'		=> $this->upload_path,
611
				'full_path'		=> $this->upload_path.$this->file_name,
612
				'raw_name'		=> substr($this->file_name, 0, -strlen($this->file_ext)),
613
				'orig_name'		=> $this->orig_name,
614
				'client_name'		=> $this->client_name,
615
				'file_ext'		=> $this->file_ext,
616
				'file_size'		=> $this->file_size,
617
				'is_image'		=> $this->is_image(),
618
				'image_width'		=> $this->image_width,
619
				'image_height'		=> $this->image_height,
620
				'image_type'		=> $this->image_type,
621
				'image_size_str'	=> $this->image_size_str,
622
			);
623
624
		if ( ! empty($index))
625
		{
626
			return isset($data[$index]) ? $data[$index] : NULL;
627
		}
628
629
		return $data;
630
	}
631
632
	// --------------------------------------------------------------------
633
634
	/**
635
	 * Set Upload Path
636
	 *
637
	 * @param	string	$path
638
	 * @return	CI_Upload
639
	 */
640
	public function set_upload_path($path)
641
	{
642
		// Make sure it has a trailing slash
643
		$this->upload_path = rtrim($path, '/').'/';
644
		return $this;
645
	}
646
647
	// --------------------------------------------------------------------
648
649
	/**
650
	 * Set the file name
651
	 *
652
	 * This function takes a filename/path as input and looks for the
653
	 * existence of a file with the same name. If found, it will append a
654
	 * number to the end of the filename to avoid overwriting a pre-existing file.
655
	 *
656
	 * @param	string	$path
657
	 * @param	string	$filename
658
	 * @return	string
659
	 *
660
	 * @codeCoverageIgnore
661
	 */
662
	public function set_filename($path, $filename)
663
	{
664
		if ($this->encrypt_name === TRUE)
665
		{
666
			$filename = md5(uniqid(mt_rand())).$this->file_ext;
667
		}
668
669
		if ($this->overwrite === TRUE OR ! file_exists($path.$filename))
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or 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...
670
		{
671
			return $filename;
672
		}
673
674
		$filename = str_replace($this->file_ext, '', $filename);
675
676
		$new_filename = '';
677
		for ($i = 1; $i < $this->max_filename_increment; $i++)
678
		{
679
			if ( ! file_exists($path.$filename.$i.$this->file_ext))
680
			{
681
				$new_filename = $filename.$i.$this->file_ext;
682
				break;
683
			}
684
		}
685
686
		if ($new_filename === '')
687
		{
688
			$this->set_error('upload_bad_filename', 'debug');
689
			return FALSE;
690
		}
691
692
		return $new_filename;
693
	}
694
695
	// --------------------------------------------------------------------
696
697
	/**
698
	 * Set Maximum File Size
699
	 *
700
	 * @param	int	$n
701
	 * @return	CI_Upload
702
	 */
703
	public function set_max_filesize($n)
704
	{
705
		$this->max_size = ($n < 0) ? 0 : (int) $n;
706
		return $this;
707
	}
708
709
	// --------------------------------------------------------------------
710
711
	/**
712
	 * Set Maximum File Size
713
	 *
714
	 * An internal alias to set_max_filesize() to help with configuration
715
	 * as initialize() will look for a set_<property_name>() method ...
716
	 *
717
	 * @param	int	$n
718
	 * @return	CI_Upload
719
	 */
720
	protected function set_max_size($n)
721
	{
722
		return $this->set_max_filesize($n);
723
	}
724
725
	// --------------------------------------------------------------------
726
727
	/**
728
	 * Set Maximum File Name Length
729
	 *
730
	 * @param	int	$n
731
	 * @return	CI_Upload
732
	 *
733
	 * @codeCoverageIgnore
734
	 */
735
	public function set_max_filename($n)
736
	{
737
		$this->max_filename = ($n < 0) ? 0 : (int) $n;
738
		return $this;
739
	}
740
741
	// --------------------------------------------------------------------
742
743
	/**
744
	 * Set Maximum Image Width
745
	 *
746
	 * @param	int	$n
747
	 * @return	CI_Upload
748
	 */
749
	public function set_max_width($n)
750
	{
751
		$this->max_width = ($n < 0) ? 0 : (int) $n;
752
		return $this;
753
	}
754
755
	// --------------------------------------------------------------------
756
757
	/**
758
	 * Set Maximum Image Height
759
	 *
760
	 * @param	int	$n
761
	 * @return	CI_Upload
762
	 */
763
	public function set_max_height($n)
764
	{
765
		$this->max_height = ($n < 0) ? 0 : (int) $n;
766
		return $this;
767
	}
768
769
	// --------------------------------------------------------------------
770
771
	/**
772
	 * Set minimum image width
773
	 *
774
	 * @param	int	$n
775
	 * @return	CI_Upload
776
	 *
777
	 * @codeCoverageIgnore
778
	 */
779
	public function set_min_width($n)
780
	{
781
		$this->min_width = ($n < 0) ? 0 : (int) $n;
782
		return $this;
783
	}
784
785
	// --------------------------------------------------------------------
786
787
	/**
788
	 * Set minimum image height
789
	 *
790
	 * @param	int	$n
791
	 * @return	CI_Upload
792
	 *
793
	 * @codeCoverageIgnore
794
	 */
795
	public function set_min_height($n)
796
	{
797
		$this->min_height = ($n < 0) ? 0 : (int) $n;
798
		return $this;
799
	}
800
801
	// --------------------------------------------------------------------
802
803
	/**
804
	 * Set Allowed File Types
805
	 *
806
	 * @param	mixed	$types
807
	 * @return	CI_Upload
808
	 */
809
	public function set_allowed_types($types)
810
	{
811
		$this->allowed_types = (is_array($types) OR $types === '*')
0 ignored issues
show
Documentation Bug introduced by
It seems like (is_array($types) or $ty... : explode('|', $types) can also be of type array. However, the property $allowed_types is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
Comprehensibility Best Practice introduced by
Using logical operators such as or 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...
812
			? $types
813
			: explode('|', $types);
814
		return $this;
815
	}
816
817
	// --------------------------------------------------------------------
818
819
	/**
820
	 * Set Image Properties
821
	 *
822
	 * Uses GD to determine the width/height/type of image
823
	 *
824
	 * @param	string	$path
825
	 * @return	CI_Upload
826
	 */
827
	public function set_image_properties($path = '')
828
	{
829
		if ($this->is_image() && function_exists('getimagesize'))
830
		{
831
			if (FALSE !== ($D = @getimagesize($path)))
832
			{
833
				$types = array(1 => 'gif', 2 => 'jpeg', 3 => 'png');
834
835
				$this->image_width	= $D[0];
836
				$this->image_height	= $D[1];
837
				$this->image_type	= isset($types[$D[2]]) ? $types[$D[2]] : 'unknown';
838
				$this->image_size_str	= $D[3]; // string containing height and width
839
			}
840
		}
841
842
		return $this;
843
	}
844
845
	// --------------------------------------------------------------------
846
847
	/**
848
	 * Set XSS Clean
849
	 *
850
	 * Enables the XSS flag so that the file that was uploaded
851
	 * will be run through the XSS filter.
852
	 *
853
	 * @param	bool	$flag
854
	 * @return	CI_Upload
855
	 *
856
	 * @codeCoverageIgnore
857
	 */
858
	public function set_xss_clean($flag = FALSE)
859
	{
860
		$this->xss_clean = ($flag === TRUE);
861
		return $this;
862
	}
863
864
	// --------------------------------------------------------------------
865
866
	/**
867
	 * Validate the image
868
	 *
869
	 * @return	bool
870
	 *
871
	 * @codeCoverageIgnore
872
	 */
873
	public function is_image()
874
	{
875
		// IE will sometimes return odd mime-types during upload, so here we just standardize all
876
		// jpegs or pngs to the same file type.
877
878
		$png_mimes  = array('image/x-png');
879
		$jpeg_mimes = array('image/jpg', 'image/jpe', 'image/jpeg', 'image/pjpeg');
880
881
		if (in_array($this->file_type, $png_mimes))
882
		{
883
			$this->file_type = 'image/png';
884
		}
885
		elseif (in_array($this->file_type, $jpeg_mimes))
886
		{
887
			$this->file_type = 'image/jpeg';
888
		}
889
890
		$img_mimes = array('image/gif',	'image/jpeg', 'image/png');
891
892
		return in_array($this->file_type, $img_mimes, TRUE);
893
	}
894
895
	// --------------------------------------------------------------------
896
897
	/**
898
	 * Verify that the filetype is allowed
899
	 *
900
	 * @param	bool	$ignore_mime
901
	 * @return	bool
902
	 *
903
	 * @codeCoverageIgnore
904
	 */
905
	public function is_allowed_filetype($ignore_mime = FALSE)
906
	{
907
		if ($this->allowed_types === '*')
908
		{
909
			return TRUE;
910
		}
911
912
		if (empty($this->allowed_types) OR ! is_array($this->allowed_types))
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or 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...
913
		{
914
			$this->set_error('upload_no_file_types', 'debug');
915
			return FALSE;
916
		}
917
918
		$ext = strtolower(ltrim($this->file_ext, '.'));
919
920
		if ( ! in_array($ext, $this->allowed_types, TRUE))
921
		{
922
			return FALSE;
923
		}
924
925
		// Images get some additional checks
926
		if (in_array($ext, array('gif', 'jpg', 'jpeg', 'jpe', 'png'), TRUE) && @getimagesize($this->file_temp) === FALSE)
927
		{
928
			return FALSE;
929
		}
930
931
		if ($ignore_mime === TRUE)
932
		{
933
			return TRUE;
934
		}
935
936
		if (isset($this->_mimes[$ext]))
937
		{
938
			return is_array($this->_mimes[$ext])
939
				? in_array($this->file_type, $this->_mimes[$ext], TRUE)
940
				: ($this->_mimes[$ext] === $this->file_type);
941
		}
942
943
		return FALSE;
944
	}
945
946
	// --------------------------------------------------------------------
947
948
	/**
949
	 * Verify that the file is within the allowed size
950
	 *
951
	 * @return	bool
952
	 *
953
	 * @codeCoverageIgnore
954
	 */
955
	public function is_allowed_filesize()
956
	{
957
		return ($this->max_size === 0 OR $this->max_size > $this->file_size);
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or 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...
958
	}
959
960
	// --------------------------------------------------------------------
961
962
	/**
963
	 * Verify that the image is within the allowed width/height
964
	 *
965
	 * @return	bool
966
	 *
967
	 * @codeCoverageIgnore
968
	 */
969
	public function is_allowed_dimensions()
970
	{
971
		if ( ! $this->is_image())
972
		{
973
			return TRUE;
974
		}
975
976
		if (function_exists('getimagesize'))
977
		{
978
			$D = @getimagesize($this->file_temp);
979
980
			if ($this->max_width > 0 && $D[0] > $this->max_width)
981
			{
982
				return FALSE;
983
			}
984
985
			if ($this->max_height > 0 && $D[1] > $this->max_height)
986
			{
987
				return FALSE;
988
			}
989
990
			if ($this->min_width > 0 && $D[0] < $this->min_width)
991
			{
992
				return FALSE;
993
			}
994
995
			if ($this->min_height > 0 && $D[1] < $this->min_height)
996
			{
997
				return FALSE;
998
			}
999
		}
1000
1001
		return TRUE;
1002
	}
1003
1004
	// --------------------------------------------------------------------
1005
1006
	/**
1007
	 * Validate Upload Path
1008
	 *
1009
	 * Verifies that it is a valid upload path with proper permissions.
1010
	 *
1011
	 * @return	bool
1012
	 *
1013
	 * @codeCoverageIgnore
1014
	 */
1015
	public function validate_upload_path()
1016
	{
1017
		if ($this->upload_path === '')
1018
		{
1019
			$this->set_error('upload_no_filepath', 'error');
1020
			return FALSE;
1021
		}
1022
1023
		if (realpath($this->upload_path) !== FALSE)
1024
		{
1025
			$this->upload_path = str_replace('\\', '/', realpath($this->upload_path));
1026
		}
1027
1028
		if ( ! is_dir($this->upload_path))
1029
		{
1030
			$this->set_error('upload_no_filepath', 'error');
1031
			return FALSE;
1032
		}
1033
1034
		if ( ! is_really_writable($this->upload_path))
1035
		{
1036
			$this->set_error('upload_not_writable', 'error');
1037
			return FALSE;
1038
		}
1039
1040
		$this->upload_path = preg_replace('/(.+?)\/*$/', '\\1/',  $this->upload_path);
1041
		return TRUE;
1042
	}
1043
1044
	// --------------------------------------------------------------------
1045
1046
	/**
1047
	 * Extract the file extension
1048
	 *
1049
	 * @param	string	$filename
1050
	 * @return	string
1051
	 *
1052
	 * @codeCoverageIgnore
1053
	 */
1054
	public function get_extension($filename)
1055
	{
1056
		$x = explode('.', $filename);
1057
1058
		if (count($x) === 1)
1059
		{
1060
			return '';
1061
		}
1062
1063
		$ext = ($this->file_ext_tolower) ? strtolower(end($x)) : end($x);
1064
		return '.'.$ext;
1065
	}
1066
1067
	// --------------------------------------------------------------------
1068
1069
	/**
1070
	 * Limit the File Name Length
1071
	 *
1072
	 * @param	string	$filename
1073
	 * @param	int	$length
1074
	 * @return	string
1075
	 *
1076
	 * @codeCoverageIgnore
1077
	 */
1078
	public function limit_filename_length($filename, $length)
1079
	{
1080
		if (strlen($filename) < $length)
1081
		{
1082
			return $filename;
1083
		}
1084
1085
		$ext = '';
1086
		if (strpos($filename, '.') !== FALSE)
1087
		{
1088
			$parts		= explode('.', $filename);
1089
			$ext		= '.'.array_pop($parts);
1090
			$filename	= implode('.', $parts);
1091
		}
1092
1093
		return substr($filename, 0, ($length - strlen($ext))).$ext;
1094
	}
1095
1096
	// --------------------------------------------------------------------
1097
1098
	/**
1099
	 * Runs the file through the XSS clean function
1100
	 *
1101
	 * This prevents people from embedding malicious code in their files.
1102
	 * I'm not sure that it won't negatively affect certain files in unexpected ways,
1103
	 * but so far I haven't found that it causes trouble.
1104
	 *
1105
	 * @return	string
1106
	 *
1107
	 * @codeCoverageIgnore
1108
	 */
1109
	public function do_xss_clean()
1110
	{
1111
		$file = $this->file_temp;
1112
1113
		if (filesize($file) == 0)
1114
		{
1115
			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 CI_Upload::do_xss_clean 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...
1116
		}
1117
1118
		if (memory_get_usage() && ($memory_limit = ini_get('memory_limit')) > 0)
1119
		{
1120
			$memory_limit = str_split($memory_limit, strspn($memory_limit, '1234567890'));
1121
			if ( ! empty($memory_limit[1]))
1122
			{
1123
				switch ($memory_limit[1][0])
1124
				{
1125
					case 'g':
1126
					case 'G':
1127
						$memory_limit[0] *= 1024 * 1024 * 1024;
1128
						break;
1129
					case 'm':
1130
					case 'M':
1131
						$memory_limit[0] *= 1024 * 1024;
1132
						break;
1133
					default:
1134
						break;
1135
				}
1136
			}
1137
1138
			$memory_limit = (int) ceil(filesize($file) + $memory_limit[0]);
1139
			ini_set('memory_limit', $memory_limit); // When an integer is used, the value is measured in bytes. - PHP.net
1140
		}
1141
1142
		// If the file being uploaded is an image, then we should have no problem with XSS attacks (in theory), but
1143
		// IE can be fooled into mime-type detecting a malformed image as an html file, thus executing an XSS attack on anyone
1144
		// using IE who looks at the image. It does this by inspecting the first 255 bytes of an image. To get around this
1145
		// CI will itself look at the first 255 bytes of an image to determine its relative safety. This can save a lot of
1146
		// processor power and time if it is actually a clean image, as it will be in nearly all instances _except_ an
1147
		// attempted XSS attack.
1148
1149
		if (function_exists('getimagesize') && @getimagesize($file) !== FALSE)
1150
		{
1151
			if (($file = @fopen($file, 'rb')) === FALSE) // "b" to force binary
1152
			{
1153
				return FALSE; // Couldn't open the file, 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 CI_Upload::do_xss_clean 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...
1154
			}
1155
1156
			$opening_bytes = fread($file, 256);
1157
			fclose($file);
1158
1159
			// These are known to throw IE into mime-type detection chaos
1160
			// <a, <body, <head, <html, <img, <plaintext, <pre, <script, <table, <title
1161
			// title is basically just in SVG, but we filter it anyhow
1162
1163
			// if it's an image or no "triggers" detected in the first 256 bytes - we're good
1164
			return ! preg_match('/<(a|body|head|html|img|plaintext|pre|script|table|title)[\s>]/i', $opening_bytes);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return !preg_match('/<(a...>]/i', $opening_bytes); (boolean) is incompatible with the return type documented by CI_Upload::do_xss_clean 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...
1165
		}
1166
1167
		if (($data = @file_get_contents($file)) === FALSE)
1168
		{
1169
			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 CI_Upload::do_xss_clean 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...
1170
		}
1171
1172
		return $this->_CI->security->xss_clean($data, TRUE);
1173
	}
1174
1175
	// --------------------------------------------------------------------
1176
1177
	/**
1178
	 * Set an error message
1179
	 *
1180
	 * @param	string	$msg
1181
	 * @return	CI_Upload
1182
	 *
1183
	 * @codeCoverageIgnore
1184
	 */
1185
	public function set_error($msg, $log_level = 'error')
1186
	{
1187
		$this->_CI->lang->load('upload');
1188
1189
		is_array($msg) OR $msg = array($msg);
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or 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...
1190
		foreach ($msg as $val)
1191
		{
1192
			$msg = ($this->_CI->lang->line($val) === FALSE) ? $val : $this->_CI->lang->line($val);
1193
			$this->error_msg[] = $msg;
1194
			log_message($log_level, $msg);
1195
		}
1196
1197
		return $this;
1198
	}
1199
1200
	// --------------------------------------------------------------------
1201
1202
	/**
1203
	 * Display the error message
1204
	 *
1205
	 * @param	string	$open
1206
	 * @param	string	$close
1207
	 * @return	string
1208
	 *
1209
	 * @codeCoverageIgnore
1210
	 */
1211
	public function display_errors($open = '<p>', $close = '</p>')
1212
	{
1213
		return (count($this->error_msg) > 0) ? $open.implode($close.$open, $this->error_msg).$close : '';
1214
	}
1215
1216
	// --------------------------------------------------------------------
1217
1218
	/**
1219
	 * Prep Filename
1220
	 *
1221
	 * Prevents possible script execution from Apache's handling
1222
	 * of files' multiple extensions.
1223
	 *
1224
	 * @link	http://httpd.apache.org/docs/1.3/mod/mod_mime.html#multipleext
1225
	 *
1226
	 * @param	string	$filename
1227
	 * @return	string
1228
	 *
1229
	 * @codeCoverageIgnore
1230
	 */
1231
	protected function _prep_filename($filename)
1232
	{
1233
		if ($this->mod_mime_fix === FALSE OR $this->allowed_types === '*' OR ($ext_pos = strrpos($filename, '.')) === FALSE)
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or 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...
1234
		{
1235
			return $filename;
1236
		}
1237
1238
		$ext = substr($filename, $ext_pos);
1239
		$filename = substr($filename, 0, $ext_pos);
1240
		return str_replace('.', '_', $filename).$ext;
1241
	}
1242
1243
	// --------------------------------------------------------------------
1244
1245
	/**
1246
	 * File MIME type
1247
	 *
1248
	 * Detects the (actual) MIME type of the uploaded file, if possible.
1249
	 * The input array is expected to be $_FILES[$field]
1250
	 *
1251
	 * @param	array	$file
1252
	 * @return	void
1253
	 *
1254
	 * @codeCoverageIgnore
1255
	 */
1256
	protected function _file_mime_type($file)
1257
	{
1258
		// We'll need this to validate the MIME info string (e.g. text/plain; charset=us-ascii)
1259
		$regexp = '/^([a-z\-]+\/[a-z0-9\-\.\+]+)(;\s.+)?$/';
1260
1261
		/**
1262
		 * Fileinfo extension - most reliable method
1263
		 *
1264
		 * Apparently XAMPP, CentOS, cPanel and who knows what
1265
		 * other PHP distribution channels EXPLICITLY DISABLE
1266
		 * ext/fileinfo, which is otherwise enabled by default
1267
		 * since PHP 5.3 ...
1268
		 */
1269
		if (function_exists('finfo_file'))
1270
		{
1271
			$finfo = @finfo_open(FILEINFO_MIME);
1272
			if (is_resource($finfo)) // It is possible that a FALSE value is returned, if there is no magic MIME database file found on the system
1273
			{
1274
				$mime = @finfo_file($finfo, $file['tmp_name']);
1275
				finfo_close($finfo);
1276
1277
				/* According to the comments section of the PHP manual page,
1278
				 * it is possible that this function returns an empty string
1279
				 * for some files (e.g. if they don't exist in the magic MIME database)
1280
				 */
1281
				if (is_string($mime) && preg_match($regexp, $mime, $matches))
1282
				{
1283
					$this->file_type = $matches[1];
1284
					return;
1285
				}
1286
			}
1287
		}
1288
1289
		/* This is an ugly hack, but UNIX-type systems provide a "native" way to detect the file type,
1290
		 * which is still more secure than depending on the value of $_FILES[$field]['type'], and as it
1291
		 * was reported in issue #750 (https://github.com/EllisLab/CodeIgniter/issues/750) - it's better
1292
		 * than mime_content_type() as well, hence the attempts to try calling the command line with
1293
		 * three different functions.
1294
		 *
1295
		 * Notes:
1296
		 *	- the DIRECTORY_SEPARATOR comparison ensures that we're not on a Windows system
1297
		 *	- many system admins would disable the exec(), shell_exec(), popen() and similar functions
1298
		 *	  due to security concerns, hence the function_usable() checks
1299
		 */
1300
		if (DIRECTORY_SEPARATOR !== '\\')
1301
		{
1302
			$cmd = function_exists('escapeshellarg')
1303
				? 'file --brief --mime '.escapeshellarg($file['tmp_name']).' 2>&1'
1304
				: 'file --brief --mime '.$file['tmp_name'].' 2>&1';
1305
1306
			if (function_usable('exec'))
1307
			{
1308
				/* This might look confusing, as $mime is being populated with all of the output when set in the second parameter.
1309
				 * However, we only need the last line, which is the actual return value of exec(), and as such - it overwrites
1310
				 * anything that could already be set for $mime previously. This effectively makes the second parameter a dummy
1311
				 * value, which is only put to allow us to get the return status code.
1312
				 */
1313
				$mime = @exec($cmd, $mime, $return_status);
1314
				if ($return_status === 0 && is_string($mime) && preg_match($regexp, $mime, $matches))
1315
				{
1316
					$this->file_type = $matches[1];
1317
					return;
1318
				}
1319
			}
1320
1321
			if ( ! ini_get('safe_mode') && function_usable('shell_exec'))
1322
			{
1323
				$mime = @shell_exec($cmd);
1324
				if (strlen($mime) > 0)
1325
				{
1326
					$mime = explode("\n", trim($mime));
1327
					if (preg_match($regexp, $mime[(count($mime) - 1)], $matches))
1328
					{
1329
						$this->file_type = $matches[1];
1330
						return;
1331
					}
1332
				}
1333
			}
1334
1335
			if (function_usable('popen'))
1336
			{
1337
				$proc = @popen($cmd, 'r');
1338
				if (is_resource($proc))
1339
				{
1340
					$mime = @fread($proc, 512);
1341
					@pclose($proc);
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...
1342
					if ($mime !== FALSE)
1343
					{
1344
						$mime = explode("\n", trim($mime));
1345
						if (preg_match($regexp, $mime[(count($mime) - 1)], $matches))
1346
						{
1347
							$this->file_type = $matches[1];
1348
							return;
1349
						}
1350
					}
1351
				}
1352
			}
1353
		}
1354
1355
		// Fall back to mime_content_type(), if available (still better than $_FILES[$field]['type'])
0 ignored issues
show
Unused Code Comprehensibility introduced by
42% 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...
1356
		if (function_exists('mime_content_type'))
1357
		{
1358
			$this->file_type = @mime_content_type($file['tmp_name']);
1359
			if (strlen($this->file_type) > 0) // It's possible that mime_content_type() returns FALSE or an empty string
1360
			{
1361
				return;
1362
			}
1363
		}
1364
1365
		$this->file_type = $file['type'];
1366
	}
1367
1368
}
1369