Completed
Push — master ( 6feaa4...cfb970 )
by Kenji
04:07 queued 01:58
created

CI_Upload::set_min_height()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 1
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
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 - 2016, 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 - 2016, 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
class CI_Upload {
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...
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
	public function __construct($config = array())
293
	{
294
		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...
295
296
		$this->_mimes =& get_mimes();
297
		$this->_CI =& get_instance();
298
299
		log_message('info', 'Upload Class Initialized');
300
	}
301
302
	// --------------------------------------------------------------------
303
304
	/**
305
	 * Initialize preferences
306
	 *
307
	 * @param	array	$config
308
	 * @param	bool	$reset
309
	 * @return	CI_Upload
310
	 */
311
	public function initialize(array $config = array(), $reset = TRUE)
312
	{
313
		$reflection = new ReflectionClass($this);
314
315
		if ($reset === TRUE)
316
		{
317
			$defaults = $reflection->getDefaultProperties();
318
			foreach (array_keys($defaults) as $key)
319
			{
320
				if ($key[0] === '_')
321
				{
322
					continue;
323
				}
324
325
				if (isset($config[$key]))
326
				{
327
					if ($reflection->hasMethod('set_'.$key))
328
					{
329
						$this->{'set_'.$key}($config[$key]);
330
					}
331
					else
332
					{
333
						$this->$key = $config[$key];
334
					}
335
				}
336
				else
337
				{
338
					$this->$key = $defaults[$key];
339
				}
340
			}
341
		}
342
		else
343
		{
344
			foreach ($config as $key => &$value)
345
			{
346
				if ($key[0] !== '_' && $reflection->hasProperty($key))
347
				{
348
					if ($reflection->hasMethod('set_'.$key))
349
					{
350
						$this->{'set_'.$key}($value);
351
					}
352
					else
353
					{
354
						$this->$key = $value;
355
					}
356
				}
357
			}
358
		}
359
360
		// if a file_name was provided in the config, use it instead of the user input
361
		// supplied file name for all uploads until initialized again
362
		$this->_file_name_override = $this->file_name;
363
		return $this;
364
	}
365
366
	// --------------------------------------------------------------------
367
368
	/**
369
	 * Perform the file upload
370
	 *
371
	 * @param	string	$field
372
	 * @return	bool
373
	 */
374
	public function do_upload($field = 'userfile')
0 ignored issues
show
Coding Style introduced by
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...
375
	{
376
		// Is $_FILES[$field] set? If not, no reason to continue.
377
		if (isset($_FILES[$field]))
378
		{
379
			$_file = $_FILES[$field];
380
		}
381
		// Does the field name contain array notation?
382 View Code Duplication
		elseif (($c = preg_match_all('/(?:^[^\[]+)|\[[^]]*\]/', $field, $matches)) > 1)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
383
		{
384
			$_file = $_FILES;
385
			for ($i = 0; $i < $c; $i++)
386
			{
387
				// We can't track numeric iterations, only full field names are accepted
388
				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...
389
				{
390
					$_file = NULL;
391
					break;
392
				}
393
394
				$_file = $_file[$field];
395
			}
396
		}
397
398
		if ( ! isset($_file))
399
		{
400
			$this->set_error('upload_no_file_selected', 'debug');
401
			return FALSE;
402
		}
403
404
		// Is the upload path valid?
405
		if ( ! $this->validate_upload_path())
406
		{
407
			// errors will already be set by validate_upload_path() so just return FALSE
408
			return FALSE;
409
		}
410
411
		// Was the file able to be uploaded? If not, determine the reason why.
412
		if ( ! file_exists($_file['tmp_name']))	// modified by ci-phpunit-test
413
		{
414
			$error = isset($_file['error']) ? $_file['error'] : 4;
415
416
			switch ($error)
417
			{
418
				case UPLOAD_ERR_INI_SIZE:
419
					$this->set_error('upload_file_exceeds_limit', 'info');
420
					break;
421
				case UPLOAD_ERR_FORM_SIZE:
422
					$this->set_error('upload_file_exceeds_form_limit', 'info');
423
					break;
424
				case UPLOAD_ERR_PARTIAL:
425
					$this->set_error('upload_file_partial', 'debug');
426
					break;
427
				case UPLOAD_ERR_NO_FILE:
428
					$this->set_error('upload_no_file_selected', 'debug');
429
					break;
430
				case UPLOAD_ERR_NO_TMP_DIR:
431
					$this->set_error('upload_no_temp_directory', 'error');
432
					break;
433
				case UPLOAD_ERR_CANT_WRITE:
434
					$this->set_error('upload_unable_to_write_file', 'error');
435
					break;
436
				case UPLOAD_ERR_EXTENSION:
437
					$this->set_error('upload_stopped_by_extension', 'debug');
438
					break;
439
				default:
440
					$this->set_error('upload_no_file_selected', 'debug');
441
					break;
442
			}
443
444
			return FALSE;
445
		}
446
447
		// Set the uploaded data as class variables
448
		$this->file_temp = $_file['tmp_name'];
449
		$this->file_size = $_file['size'];
450
451
		// Skip MIME type detection?
452
		if ($this->detect_mime !== FALSE)
453
		{
454
			$this->_file_mime_type($_file);
455
		}
456
457
		$this->file_type = preg_replace('/^(.+?);.*$/', '\\1', $this->file_type);
458
		$this->file_type = strtolower(trim(stripslashes($this->file_type), '"'));
459
		$this->file_name = $this->_prep_filename($_file['name']);
460
		$this->file_ext	 = $this->get_extension($this->file_name);
461
		$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...
462
463
		// Is the file type allowed to be uploaded?
464
		if ( ! $this->is_allowed_filetype())
465
		{
466
			$this->set_error('upload_invalid_filetype', 'debug');
467
			return FALSE;
468
		}
469
470
		// if we're overriding, let's now make sure the new name and type is allowed
471
		if ($this->_file_name_override !== '')
472
		{
473
			$this->file_name = $this->_prep_filename($this->_file_name_override);
474
475
			// If no extension was provided in the file_name config item, use the uploaded one
476
			if (strpos($this->_file_name_override, '.') === FALSE)
477
			{
478
				$this->file_name .= $this->file_ext;
479
			}
480
			else
481
			{
482
				// An extension was provided, let's have it!
483
				$this->file_ext	= $this->get_extension($this->_file_name_override);
484
			}
485
486
			if ( ! $this->is_allowed_filetype(TRUE))
487
			{
488
				$this->set_error('upload_invalid_filetype', 'debug');
489
				return FALSE;
490
			}
491
		}
492
493
		// Convert the file size to kilobytes
494
		if ($this->file_size > 0)
495
		{
496
			$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...
497
		}
498
499
		// Is the file size within the allowed maximum?
500
		if ( ! $this->is_allowed_filesize())
501
		{
502
			$this->set_error('upload_invalid_filesize', 'info');
503
			return FALSE;
504
		}
505
506
		// Are the image dimensions within the allowed size?
507
		// Note: This can fail if the server has an open_basedir restriction.
508
		if ( ! $this->is_allowed_dimensions())
509
		{
510
			$this->set_error('upload_invalid_dimensions', 'info');
511
			return FALSE;
512
		}
513
514
		// Sanitize the file name for security
515
		$this->file_name = $this->_CI->security->sanitize_filename($this->file_name);
516
517
		// Truncate the file name if it's too long
518
		if ($this->max_filename > 0)
519
		{
520
			$this->file_name = $this->limit_filename_length($this->file_name, $this->max_filename);
521
		}
522
523
		// Remove white spaces in the name
524
		if ($this->remove_spaces === TRUE)
525
		{
526
			$this->file_name = preg_replace('/\s+/', '_', $this->file_name);
527
		}
528
529
		if ($this->file_ext_tolower && ($ext_length = strlen($this->file_ext)))
530
		{
531
			// file_ext was previously lower-cased by a get_extension() call
532
			$this->file_name = substr($this->file_name, 0, -$ext_length).$this->file_ext;
533
		}
534
535
		/*
536
		 * Validate the file name
537
		 * This function appends an number onto the end of
538
		 * the file if one with the same name already exists.
539
		 * If it returns false there was a problem.
540
		 */
541
		$this->orig_name = $this->file_name;
542
		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...
543
		{
544
			return FALSE;
545
		}
546
547
		/*
548
		 * Run the file through the XSS hacking filter
549
		 * This helps prevent malicious code from being
550
		 * embedded within a file. Scripts can easily
551
		 * be disguised as images or other file types.
552
		 */
553
		if ($this->xss_clean && $this->do_xss_clean() === FALSE)
554
		{
555
			$this->set_error('upload_unable_to_write_file', 'error');
556
			return FALSE;
557
		}
558
559
		/*
560
		 * Move the file to the final destination
561
		 * To deal with different server configurations
562
		 * we'll attempt to use copy() first. If that fails
563
		 * we'll use move_uploaded_file(). One of the two should
564
		 * reliably work in most environments
565
		 */
566
		if ( ! @copy($this->file_temp, $this->upload_path.$this->file_name))
567
		{
568
			if ( ! @move_uploaded_file($this->file_temp, $this->upload_path.$this->file_name))
569
			{
570
				$this->set_error('upload_destination_error', 'error');
571
				return FALSE;
572
			}
573
		}
574
575
		/*
576
		 * Set the finalized image dimensions
577
		 * This sets the image width/height (assuming the
578
		 * file was an image). We use this information
579
		 * in the "data" function.
580
		 */
581
		$this->set_image_properties($this->upload_path.$this->file_name);
582
583
		return TRUE;
584
	}
585
586
	// --------------------------------------------------------------------
587
588
	/**
589
	 * Finalized Data Array
590
	 *
591
	 * Returns an associative array containing all of the information
592
	 * related to the upload, allowing the developer easy access in one array.
593
	 *
594
	 * @param	string	$index
595
	 * @return	mixed
596
	 */
597
	public function data($index = NULL)
598
	{
599
		$data = array(
600
				'file_name'		=> $this->file_name,
601
				'file_type'		=> $this->file_type,
602
				'file_path'		=> $this->upload_path,
603
				'full_path'		=> $this->upload_path.$this->file_name,
604
				'raw_name'		=> substr($this->file_name, 0, -strlen($this->file_ext)),
605
				'orig_name'		=> $this->orig_name,
606
				'client_name'		=> $this->client_name,
607
				'file_ext'		=> $this->file_ext,
608
				'file_size'		=> $this->file_size,
609
				'is_image'		=> $this->is_image(),
610
				'image_width'		=> $this->image_width,
611
				'image_height'		=> $this->image_height,
612
				'image_type'		=> $this->image_type,
613
				'image_size_str'	=> $this->image_size_str,
614
			);
615
616
		if ( ! empty($index))
617
		{
618
			return isset($data[$index]) ? $data[$index] : NULL;
619
		}
620
621
		return $data;
622
	}
623
624
	// --------------------------------------------------------------------
625
626
	/**
627
	 * Set Upload Path
628
	 *
629
	 * @param	string	$path
630
	 * @return	CI_Upload
631
	 */
632
	public function set_upload_path($path)
633
	{
634
		// Make sure it has a trailing slash
635
		$this->upload_path = rtrim($path, '/').'/';
636
		return $this;
637
	}
638
639
	// --------------------------------------------------------------------
640
641
	/**
642
	 * Set the file name
643
	 *
644
	 * This function takes a filename/path as input and looks for the
645
	 * existence of a file with the same name. If found, it will append a
646
	 * number to the end of the filename to avoid overwriting a pre-existing file.
647
	 *
648
	 * @param	string	$path
649
	 * @param	string	$filename
650
	 * @return	string
651
	 */
652
	public function set_filename($path, $filename)
653
	{
654
		if ($this->encrypt_name === TRUE)
655
		{
656
			$filename = md5(uniqid(mt_rand())).$this->file_ext;
657
		}
658
659
		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...
660
		{
661
			return $filename;
662
		}
663
664
		$filename = str_replace($this->file_ext, '', $filename);
665
666
		$new_filename = '';
667
		for ($i = 1; $i < $this->max_filename_increment; $i++)
668
		{
669
			if ( ! file_exists($path.$filename.$i.$this->file_ext))
670
			{
671
				$new_filename = $filename.$i.$this->file_ext;
672
				break;
673
			}
674
		}
675
676
		if ($new_filename === '')
677
		{
678
			$this->set_error('upload_bad_filename', 'debug');
679
			return FALSE;
680
		}
681
		else
682
		{
683
			return $new_filename;
684
		}
685
	}
686
687
	// --------------------------------------------------------------------
688
689
	/**
690
	 * Set Maximum File Size
691
	 *
692
	 * @param	int	$n
693
	 * @return	CI_Upload
694
	 */
695
	public function set_max_filesize($n)
696
	{
697
		$this->max_size = ($n < 0) ? 0 : (int) $n;
698
		return $this;
699
	}
700
701
	// --------------------------------------------------------------------
702
703
	/**
704
	 * Set Maximum File Size
705
	 *
706
	 * An internal alias to set_max_filesize() to help with configuration
707
	 * as initialize() will look for a set_<property_name>() method ...
708
	 *
709
	 * @param	int	$n
710
	 * @return	CI_Upload
711
	 */
712
	protected function set_max_size($n)
713
	{
714
		return $this->set_max_filesize($n);
715
	}
716
717
	// --------------------------------------------------------------------
718
719
	/**
720
	 * Set Maximum File Name Length
721
	 *
722
	 * @param	int	$n
723
	 * @return	CI_Upload
724
	 */
725
	public function set_max_filename($n)
726
	{
727
		$this->max_filename = ($n < 0) ? 0 : (int) $n;
728
		return $this;
729
	}
730
731
	// --------------------------------------------------------------------
732
733
	/**
734
	 * Set Maximum Image Width
735
	 *
736
	 * @param	int	$n
737
	 * @return	CI_Upload
738
	 */
739
	public function set_max_width($n)
740
	{
741
		$this->max_width = ($n < 0) ? 0 : (int) $n;
742
		return $this;
743
	}
744
745
	// --------------------------------------------------------------------
746
747
	/**
748
	 * Set Maximum Image Height
749
	 *
750
	 * @param	int	$n
751
	 * @return	CI_Upload
752
	 */
753
	public function set_max_height($n)
754
	{
755
		$this->max_height = ($n < 0) ? 0 : (int) $n;
756
		return $this;
757
	}
758
759
	// --------------------------------------------------------------------
760
761
	/**
762
	 * Set minimum image width
763
	 *
764
	 * @param	int	$n
765
	 * @return	CI_Upload
766
	 */
767
	public function set_min_width($n)
768
	{
769
		$this->min_width = ($n < 0) ? 0 : (int) $n;
770
		return $this;
771
	}
772
773
	// --------------------------------------------------------------------
774
775
	/**
776
	 * Set minimum image height
777
	 *
778
	 * @param	int	$n
779
	 * @return	CI_Upload
780
	 */
781
	public function set_min_height($n)
782
	{
783
		$this->min_height = ($n < 0) ? 0 : (int) $n;
784
		return $this;
785
	}
786
787
	// --------------------------------------------------------------------
788
789
	/**
790
	 * Set Allowed File Types
791
	 *
792
	 * @param	mixed	$types
793
	 * @return	CI_Upload
794
	 */
795
	public function set_allowed_types($types)
796
	{
797
		$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...
798
			? $types
799
			: explode('|', $types);
800
		return $this;
801
	}
802
803
	// --------------------------------------------------------------------
804
805
	/**
806
	 * Set Image Properties
807
	 *
808
	 * Uses GD to determine the width/height/type of image
809
	 *
810
	 * @param	string	$path
811
	 * @return	CI_Upload
812
	 */
813
	public function set_image_properties($path = '')
814
	{
815
		if ($this->is_image() && function_exists('getimagesize'))
816
		{
817
			if (FALSE !== ($D = @getimagesize($path)))
818
			{
819
				$types = array(1 => 'gif', 2 => 'jpeg', 3 => 'png');
820
821
				$this->image_width	= $D[0];
822
				$this->image_height	= $D[1];
823
				$this->image_type	= isset($types[$D[2]]) ? $types[$D[2]] : 'unknown';
824
				$this->image_size_str	= $D[3]; // string containing height and width
825
			}
826
		}
827
828
		return $this;
829
	}
830
831
	// --------------------------------------------------------------------
832
833
	/**
834
	 * Set XSS Clean
835
	 *
836
	 * Enables the XSS flag so that the file that was uploaded
837
	 * will be run through the XSS filter.
838
	 *
839
	 * @param	bool	$flag
840
	 * @return	CI_Upload
841
	 */
842
	public function set_xss_clean($flag = FALSE)
843
	{
844
		$this->xss_clean = ($flag === TRUE);
845
		return $this;
846
	}
847
848
	// --------------------------------------------------------------------
849
850
	/**
851
	 * Validate the image
852
	 *
853
	 * @return	bool
854
	 */
855
	public function is_image()
856
	{
857
		// IE will sometimes return odd mime-types during upload, so here we just standardize all
858
		// jpegs or pngs to the same file type.
859
860
		$png_mimes  = array('image/x-png');
861
		$jpeg_mimes = array('image/jpg', 'image/jpe', 'image/jpeg', 'image/pjpeg');
862
863
		if (in_array($this->file_type, $png_mimes))
864
		{
865
			$this->file_type = 'image/png';
866
		}
867
		elseif (in_array($this->file_type, $jpeg_mimes))
868
		{
869
			$this->file_type = 'image/jpeg';
870
		}
871
872
		$img_mimes = array('image/gif',	'image/jpeg', 'image/png');
873
874
		return in_array($this->file_type, $img_mimes, TRUE);
875
	}
876
877
	// --------------------------------------------------------------------
878
879
	/**
880
	 * Verify that the filetype is allowed
881
	 *
882
	 * @param	bool	$ignore_mime
883
	 * @return	bool
884
	 */
885
	public function is_allowed_filetype($ignore_mime = FALSE)
886
	{
887
		if ($this->allowed_types === '*')
888
		{
889
			return TRUE;
890
		}
891
892
		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...
893
		{
894
			$this->set_error('upload_no_file_types', 'debug');
895
			return FALSE;
896
		}
897
898
		$ext = strtolower(ltrim($this->file_ext, '.'));
899
900
		if ( ! in_array($ext, $this->allowed_types, TRUE))
901
		{
902
			return FALSE;
903
		}
904
905
		// Images get some additional checks
906
		if (in_array($ext, array('gif', 'jpg', 'jpeg', 'jpe', 'png'), TRUE) && @getimagesize($this->file_temp) === FALSE)
907
		{
908
			return FALSE;
909
		}
910
911
		if ($ignore_mime === TRUE)
912
		{
913
			return TRUE;
914
		}
915
916
		if (isset($this->_mimes[$ext]))
917
		{
918
			return is_array($this->_mimes[$ext])
919
				? in_array($this->file_type, $this->_mimes[$ext], TRUE)
920
				: ($this->_mimes[$ext] === $this->file_type);
921
		}
922
923
		return FALSE;
924
	}
925
926
	// --------------------------------------------------------------------
927
928
	/**
929
	 * Verify that the file is within the allowed size
930
	 *
931
	 * @return	bool
932
	 */
933
	public function is_allowed_filesize()
934
	{
935
		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...
936
	}
937
938
	// --------------------------------------------------------------------
939
940
	/**
941
	 * Verify that the image is within the allowed width/height
942
	 *
943
	 * @return	bool
944
	 */
945
	public function is_allowed_dimensions()
946
	{
947
		if ( ! $this->is_image())
948
		{
949
			return TRUE;
950
		}
951
952
		if (function_exists('getimagesize'))
953
		{
954
			$D = @getimagesize($this->file_temp);
955
956
			if ($this->max_width > 0 && $D[0] > $this->max_width)
957
			{
958
				return FALSE;
959
			}
960
961
			if ($this->max_height > 0 && $D[1] > $this->max_height)
962
			{
963
				return FALSE;
964
			}
965
966
			if ($this->min_width > 0 && $D[0] < $this->min_width)
967
			{
968
				return FALSE;
969
			}
970
971
			if ($this->min_height > 0 && $D[1] < $this->min_height)
972
			{
973
				return FALSE;
974
			}
975
		}
976
977
		return TRUE;
978
	}
979
980
	// --------------------------------------------------------------------
981
982
	/**
983
	 * Validate Upload Path
984
	 *
985
	 * Verifies that it is a valid upload path with proper permissions.
986
	 *
987
	 * @return	bool
988
	 */
989
	public function validate_upload_path()
990
	{
991
		if ($this->upload_path === '')
992
		{
993
			$this->set_error('upload_no_filepath', 'error');
994
			return FALSE;
995
		}
996
997
		if (realpath($this->upload_path) !== FALSE)
998
		{
999
			$this->upload_path = str_replace('\\', '/', realpath($this->upload_path));
1000
		}
1001
1002
		if ( ! is_dir($this->upload_path))
1003
		{
1004
			$this->set_error('upload_no_filepath', 'error');
1005
			return FALSE;
1006
		}
1007
1008
		if ( ! is_really_writable($this->upload_path))
1009
		{
1010
			$this->set_error('upload_not_writable', 'error');
1011
			return FALSE;
1012
		}
1013
1014
		$this->upload_path = preg_replace('/(.+?)\/*$/', '\\1/',  $this->upload_path);
1015
		return TRUE;
1016
	}
1017
1018
	// --------------------------------------------------------------------
1019
1020
	/**
1021
	 * Extract the file extension
1022
	 *
1023
	 * @param	string	$filename
1024
	 * @return	string
1025
	 */
1026
	public function get_extension($filename)
1027
	{
1028
		$x = explode('.', $filename);
1029
1030
		if (count($x) === 1)
1031
		{
1032
			return '';
1033
		}
1034
1035
		$ext = ($this->file_ext_tolower) ? strtolower(end($x)) : end($x);
1036
		return '.'.$ext;
1037
	}
1038
1039
	// --------------------------------------------------------------------
1040
1041
	/**
1042
	 * Limit the File Name Length
1043
	 *
1044
	 * @param	string	$filename
1045
	 * @param	int	$length
1046
	 * @return	string
1047
	 */
1048
	public function limit_filename_length($filename, $length)
1049
	{
1050
		if (strlen($filename) < $length)
1051
		{
1052
			return $filename;
1053
		}
1054
1055
		$ext = '';
1056
		if (strpos($filename, '.') !== FALSE)
1057
		{
1058
			$parts		= explode('.', $filename);
1059
			$ext		= '.'.array_pop($parts);
1060
			$filename	= implode('.', $parts);
1061
		}
1062
1063
		return substr($filename, 0, ($length - strlen($ext))).$ext;
1064
	}
1065
1066
	// --------------------------------------------------------------------
1067
1068
	/**
1069
	 * Runs the file through the XSS clean function
1070
	 *
1071
	 * This prevents people from embedding malicious code in their files.
1072
	 * I'm not sure that it won't negatively affect certain files in unexpected ways,
1073
	 * but so far I haven't found that it causes trouble.
1074
	 *
1075
	 * @return	string
1076
	 */
1077
	public function do_xss_clean()
1078
	{
1079
		$file = $this->file_temp;
1080
1081
		if (filesize($file) == 0)
1082
		{
1083
			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...
1084
		}
1085
1086
		if (memory_get_usage() && ($memory_limit = ini_get('memory_limit')) > 0)
1087
		{
1088
			$memory_limit = str_split($memory_limit, strspn($memory_limit, '1234567890'));
1089
			if ( ! empty($memory_limit[1]))
1090
			{
1091
				switch ($memory_limit[1][0])
1092
				{
1093
					case 'g':
1094
					case 'G':
1095
						$memory_limit[0] *= 1024 * 1024 * 1024;
1096
						break;
1097
					case 'm':
1098
					case 'M':
1099
						$memory_limit[0] *= 1024 * 1024;
1100
						break;
1101
					default:
1102
						break;
1103
				}
1104
			}
1105
1106
			$memory_limit = (int) ceil(filesize($file) + $memory_limit[0]);
1107
			ini_set('memory_limit', $memory_limit); // When an integer is used, the value is measured in bytes. - PHP.net
1108
		}
1109
1110
		// If the file being uploaded is an image, then we should have no problem with XSS attacks (in theory), but
1111
		// IE can be fooled into mime-type detecting a malformed image as an html file, thus executing an XSS attack on anyone
1112
		// using IE who looks at the image. It does this by inspecting the first 255 bytes of an image. To get around this
1113
		// CI will itself look at the first 255 bytes of an image to determine its relative safety. This can save a lot of
1114
		// processor power and time if it is actually a clean image, as it will be in nearly all instances _except_ an
1115
		// attempted XSS attack.
1116
1117
		if (function_exists('getimagesize') && @getimagesize($file) !== FALSE)
1118
		{
1119
			if (($file = @fopen($file, 'rb')) === FALSE) // "b" to force binary
1120
			{
1121
				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...
1122
			}
1123
1124
			$opening_bytes = fread($file, 256);
1125
			fclose($file);
1126
1127
			// These are known to throw IE into mime-type detection chaos
1128
			// <a, <body, <head, <html, <img, <plaintext, <pre, <script, <table, <title
1129
			// title is basically just in SVG, but we filter it anyhow
1130
1131
			// if it's an image or no "triggers" detected in the first 256 bytes - we're good
1132
			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...
1133
		}
1134
1135
		if (($data = @file_get_contents($file)) === FALSE)
1136
		{
1137
			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...
1138
		}
1139
1140
		return $this->_CI->security->xss_clean($data, TRUE);
1141
	}
1142
1143
	// --------------------------------------------------------------------
1144
1145
	/**
1146
	 * Set an error message
1147
	 *
1148
	 * @param	string	$msg
1149
	 * @return	CI_Upload
1150
	 */
1151
	public function set_error($msg, $log_level = 'error')
1152
	{
1153
		$this->_CI->lang->load('upload');
1154
1155
		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...
1156
		foreach ($msg as $val)
1157
		{
1158
			$msg = ($this->_CI->lang->line($val) === FALSE) ? $val : $this->_CI->lang->line($val);
1159
			$this->error_msg[] = $msg;
1160
			log_message($log_level, $msg);
1161
		}
1162
1163
		return $this;
1164
	}
1165
1166
	// --------------------------------------------------------------------
1167
1168
	/**
1169
	 * Display the error message
1170
	 *
1171
	 * @param	string	$open
1172
	 * @param	string	$close
1173
	 * @return	string
1174
	 */
1175
	public function display_errors($open = '<p>', $close = '</p>')
1176
	{
1177
		return (count($this->error_msg) > 0) ? $open.implode($close.$open, $this->error_msg).$close : '';
1178
	}
1179
1180
	// --------------------------------------------------------------------
1181
1182
	/**
1183
	 * Prep Filename
1184
	 *
1185
	 * Prevents possible script execution from Apache's handling
1186
	 * of files' multiple extensions.
1187
	 *
1188
	 * @link	http://httpd.apache.org/docs/1.3/mod/mod_mime.html#multipleext
1189
	 *
1190
	 * @param	string	$filename
1191
	 * @return	string
1192
	 */
1193
	protected function _prep_filename($filename)
1194
	{
1195
		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...
1196
		{
1197
			return $filename;
1198
		}
1199
1200
		$ext = substr($filename, $ext_pos);
1201
		$filename = substr($filename, 0, $ext_pos);
1202
		return str_replace('.', '_', $filename).$ext;
1203
	}
1204
1205
	// --------------------------------------------------------------------
1206
1207
	/**
1208
	 * File MIME type
1209
	 *
1210
	 * Detects the (actual) MIME type of the uploaded file, if possible.
1211
	 * The input array is expected to be $_FILES[$field]
1212
	 *
1213
	 * @param	array	$file
1214
	 * @return	void
1215
	 */
1216
	protected function _file_mime_type($file)
1217
	{
1218
		// We'll need this to validate the MIME info string (e.g. text/plain; charset=us-ascii)
1219
		$regexp = '/^([a-z\-]+\/[a-z0-9\-\.\+]+)(;\s.+)?$/';
1220
1221
		// Fileinfo extension - most reliable method
1222
		$finfo = @finfo_open(FILEINFO_MIME);
1223 View Code Duplication
		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
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...
1224
		{
1225
			$mime = @finfo_file($finfo, $file['tmp_name']);
1226
			finfo_close($finfo);
1227
1228
			/* According to the comments section of the PHP manual page,
1229
			 * it is possible that this function returns an empty string
1230
			 * for some files (e.g. if they don't exist in the magic MIME database)
1231
			 */
1232
			if (is_string($mime) && preg_match($regexp, $mime, $matches))
1233
			{
1234
				$this->file_type = $matches[1];
1235
				return;
1236
			}
1237
		}
1238
1239
		/* This is an ugly hack, but UNIX-type systems provide a "native" way to detect the file type,
1240
		 * which is still more secure than depending on the value of $_FILES[$field]['type'], and as it
1241
		 * was reported in issue #750 (https://github.com/EllisLab/CodeIgniter/issues/750) - it's better
1242
		 * than mime_content_type() as well, hence the attempts to try calling the command line with
1243
		 * three different functions.
1244
		 *
1245
		 * Notes:
1246
		 *	- the DIRECTORY_SEPARATOR comparison ensures that we're not on a Windows system
1247
		 *	- many system admins would disable the exec(), shell_exec(), popen() and similar functions
1248
		 *	  due to security concerns, hence the function_usable() checks
1249
		 */
1250
		if (DIRECTORY_SEPARATOR !== '\\')
1251
		{
1252
			$cmd = function_exists('escapeshellarg')
1253
				? 'file --brief --mime '.escapeshellarg($file['tmp_name']).' 2>&1'
1254
				: 'file --brief --mime '.$file['tmp_name'].' 2>&1';
1255
1256 View Code Duplication
			if (function_usable('exec'))
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...
1257
			{
1258
				/* This might look confusing, as $mime is being populated with all of the output when set in the second parameter.
1259
				 * However, we only need the last line, which is the actual return value of exec(), and as such - it overwrites
1260
				 * anything that could already be set for $mime previously. This effectively makes the second parameter a dummy
1261
				 * value, which is only put to allow us to get the return status code.
1262
				 */
1263
				$mime = @exec($cmd, $mime, $return_status);
1264
				if ($return_status === 0 && is_string($mime) && preg_match($regexp, $mime, $matches))
1265
				{
1266
					$this->file_type = $matches[1];
1267
					return;
1268
				}
1269
			}
1270
1271
			if ( ! ini_get('safe_mode') && function_usable('shell_exec'))
1272
			{
1273
				$mime = @shell_exec($cmd);
1274 View Code Duplication
				if (strlen($mime) > 0)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1275
				{
1276
					$mime = explode("\n", trim($mime));
1277
					if (preg_match($regexp, $mime[(count($mime) - 1)], $matches))
1278
					{
1279
						$this->file_type = $matches[1];
1280
						return;
1281
					}
1282
				}
1283
			}
1284
1285
			if (function_usable('popen'))
1286
			{
1287
				$proc = @popen($cmd, 'r');
1288
				if (is_resource($proc))
1289
				{
1290
					$mime = @fread($proc, 512);
1291
					@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...
1292 View Code Duplication
					if ($mime !== 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...
1293
					{
1294
						$mime = explode("\n", trim($mime));
1295
						if (preg_match($regexp, $mime[(count($mime) - 1)], $matches))
1296
						{
1297
							$this->file_type = $matches[1];
1298
							return;
1299
						}
1300
					}
1301
				}
1302
			}
1303
		}
1304
1305
		// Fall back to the deprecated mime_content_type(), if available (still better than $_FILES[$field]['type'])
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...
1306
		if (function_exists('mime_content_type'))
1307
		{
1308
			$this->file_type = @mime_content_type($file['tmp_name']);
1309
			if (strlen($this->file_type) > 0) // It's possible that mime_content_type() returns FALSE or an empty string
1310
			{
1311
				return;
1312
			}
1313
		}
1314
1315
		$this->file_type = $file['type'];
1316
	}
1317
1318
}
1319