Completed
Push — master ( 9113ef...548441 )
by Kenji
15s queued 11s
created

CI_Input::set_cookie()   F

Complexity

Conditions 16
Paths 768

Size

Total Lines 61

Duplication

Lines 61
Ratio 100 %

Importance

Changes 0
Metric Value
cc 16
nc 768
nop 8
dl 61
loc 61
rs 1.7222
c 0
b 0
f 0

How to fix   Long Method    Complexity    Many Parameters   

Long Method

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

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

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
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 - 2019, 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 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
33
 * @license	https://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');
39
40
/**
41
 * Input Class (Modified by ci-phpunit-test)
42
 *
43
 * Pre-processes global input data for security
44
 *
45
 * @package		CodeIgniter
46
 * @subpackage	Libraries
47
 * @category	Input
48
 * @author		EllisLab Dev Team
49
 * @link		https://codeigniter.com/user_guide/libraries/input.html
50
 */
51 View Code Duplication
class CI_Input {
0 ignored issues
show
Duplication introduced by
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...
52
53
	/**
54
	 * IP address of the current user
55
	 *
56
	 * @var	string
57
	 */
58
	protected $ip_address = FALSE;
59
60
	/**
61
	 * Allow GET array flag
62
	 *
63
	 * If set to FALSE, then $_GET will be set to an empty array.
64
	 *
65
	 * @var	bool
66
	 */
67
	protected $_allow_get_array = TRUE;
68
69
	/**
70
	 * Standardize new lines flag
71
	 *
72
	 * If set to TRUE, then newlines are standardized.
73
	 *
74
	 * @var	bool
75
	 */
76
	protected $_standardize_newlines;
77
78
	/**
79
	 * Enable XSS flag
80
	 *
81
	 * Determines whether the XSS filter is always active when
82
	 * GET, POST or COOKIE data is encountered.
83
	 * Set automatically based on config setting.
84
	 *
85
	 * @var	bool
86
	 */
87
	protected $_enable_xss = FALSE;
88
89
	/**
90
	 * Enable CSRF flag
91
	 *
92
	 * Enables a CSRF cookie token to be set.
93
	 * Set automatically based on config setting.
94
	 *
95
	 * @var	bool
96
	 */
97
	protected $_enable_csrf = FALSE;
98
99
	/**
100
	 * List of all HTTP request headers
101
	 *
102
	 * @var array
103
	 */
104
	protected $headers = array();
105
106
	/**
107
	 * Raw input stream data
108
	 *
109
	 * Holds a cache of php://input contents
110
	 *
111
	 * @var	string
112
	 */
113
	protected $_raw_input_stream;
114
115
	/**
116
	 * Parsed input stream data
117
	 *
118
	 * Parsed from php://input at runtime
119
	 *
120
	 * @see	CI_Input::input_stream()
121
	 * @var	array
122
	 */
123
	protected $_input_stream;
124
125
	protected $security;
126
	protected $uni;
127
128
	// --------------------------------------------------------------------
129
130
	/**
131
	 * Class constructor
132
	 *
133
	 * Determines whether to globally enable the XSS processing
134
	 * and whether to allow the $_GET array.
135
	 *
136
	 * @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...
137
	 *
138
	 * @codeCoverageIgnore
139
	 */
140
	public function __construct()
141
	{
142
		$this->_allow_get_array		= (config_item('allow_get_array') !== FALSE);
143
		$this->_enable_xss		= (config_item('global_xss_filtering') === TRUE);
144
		$this->_enable_csrf		= (config_item('csrf_protection') === TRUE);
145
		$this->_standardize_newlines	= (bool) config_item('standardize_newlines');
146
147
		$this->security =& load_class('Security', 'core');
148
149
		// Do we need the UTF-8 class?
150
		if (UTF8_ENABLED === TRUE)
151
		{
152
			$this->uni =& load_class('Utf8', 'core');
153
		}
154
155
		// Sanitize global arrays
156
		$this->_sanitize_globals();
157
158
		// CSRF Protection check
159
		if ($this->_enable_csrf === TRUE && ! is_cli())
160
		{
161
			$this->security->csrf_verify();
162
		}
163
164
		log_message('info', 'Input Class Initialized');
165
	}
166
167
	// --------------------------------------------------------------------
168
169
	/**
170
	 * Fetch from array
171
	 *
172
	 * Internal method used to retrieve values from global arrays.
173
	 *
174
	 * @param	array	&$array		$_GET, $_POST, $_COOKIE, $_SERVER, etc.
175
	 * @param	mixed	$index		Index for item to be fetched from $array
176
	 * @param	bool	$xss_clean	Whether to apply XSS filtering
177
	 * @return	mixed
178
	 *
179
	 * @codeCoverageIgnore
180
	 */
181
	protected function _fetch_from_array(&$array, $index = NULL, $xss_clean = NULL)
182
	{
183
		is_bool($xss_clean) OR $xss_clean = $this->_enable_xss;
184
185
		// If $index is NULL, it means that the whole $array is requested
186
		isset($index) OR $index = array_keys($array);
187
188
		// allow fetching multiple keys at once
189
		if (is_array($index))
190
		{
191
			$output = array();
192
			foreach ($index as $key)
193
			{
194
				$output[$key] = $this->_fetch_from_array($array, $key, $xss_clean);
195
			}
196
197
			return $output;
198
		}
199
200
		if (isset($array[$index]))
201
		{
202
			$value = $array[$index];
203
		}
204
		elseif (($count = preg_match_all('/(?:^[^\[]+)|\[[^]]*\]/', $index, $matches)) > 1) // Does the index contain array notation
205
		{
206
			$value = $array;
207
			for ($i = 0; $i < $count; $i++)
208
			{
209
				$key = trim($matches[0][$i], '[]');
210
				if ($key === '') // Empty notation will return the value as array
211
				{
212
					break;
213
				}
214
215
				if (isset($value[$key]))
216
				{
217
					$value = $value[$key];
218
				}
219
				else
220
				{
221
					return NULL;
222
				}
223
			}
224
		}
225
		else
226
		{
227
			return NULL;
228
		}
229
230
		return ($xss_clean === TRUE)
231
			? $this->security->xss_clean($value)
232
			: $value;
233
	}
234
235
	// --------------------------------------------------------------------
236
237
	/**
238
	 * Fetch an item from the GET array
239
	 *
240
	 * @param	mixed	$index		Index for item to be fetched from $_GET
241
	 * @param	bool	$xss_clean	Whether to apply XSS filtering
242
	 * @return	mixed
243
	 *
244
	 * @codeCoverageIgnore
245
	 */
246
	public function get($index = NULL, $xss_clean = NULL)
247
	{
248
		return $this->_fetch_from_array($_GET, $index, $xss_clean);
249
	}
250
251
	// --------------------------------------------------------------------
252
253
	/**
254
	 * Fetch an item from the POST array
255
	 *
256
	 * @param	mixed	$index		Index for item to be fetched from $_POST
257
	 * @param	bool	$xss_clean	Whether to apply XSS filtering
258
	 * @return	mixed
259
	 *
260
	 * @codeCoverageIgnore
261
	 */
262
	public function post($index = NULL, $xss_clean = NULL)
263
	{
264
		return $this->_fetch_from_array($_POST, $index, $xss_clean);
265
	}
266
267
	// --------------------------------------------------------------------
268
269
	/**
270
	 * Fetch an item from POST data with fallback to GET
271
	 *
272
	 * @param	string	$index		Index for item to be fetched from $_POST or $_GET
273
	 * @param	bool	$xss_clean	Whether to apply XSS filtering
274
	 * @return	mixed
275
	 *
276
	 * @codeCoverageIgnore
277
	 */
278
	public function post_get($index, $xss_clean = NULL)
279
	{
280
		return isset($_POST[$index])
281
			? $this->post($index, $xss_clean)
282
			: $this->get($index, $xss_clean);
283
	}
284
285
	// --------------------------------------------------------------------
286
287
	/**
288
	 * Fetch an item from GET data with fallback to POST
289
	 *
290
	 * @param	string	$index		Index for item to be fetched from $_GET or $_POST
291
	 * @param	bool	$xss_clean	Whether to apply XSS filtering
292
	 * @return	mixed
293
	 *
294
	 * @codeCoverageIgnore
295
	 */
296
	public function get_post($index, $xss_clean = NULL)
297
	{
298
		return isset($_GET[$index])
299
			? $this->get($index, $xss_clean)
300
			: $this->post($index, $xss_clean);
301
	}
302
303
	// --------------------------------------------------------------------
304
305
	/**
306
	 * Fetch an item from the COOKIE array
307
	 *
308
	 * @param	mixed	$index		Index for item to be fetched from $_COOKIE
309
	 * @param	bool	$xss_clean	Whether to apply XSS filtering
310
	 * @return	mixed
311
	 *
312
	 * @codeCoverageIgnore
313
	 */
314
	public function cookie($index = NULL, $xss_clean = NULL)
315
	{
316
		return $this->_fetch_from_array($_COOKIE, $index, $xss_clean);
317
	}
318
319
	// --------------------------------------------------------------------
320
321
	/**
322
	 * Fetch an item from the SERVER array
323
	 *
324
	 * @param	mixed	$index		Index for item to be fetched from $_SERVER
325
	 * @param	bool	$xss_clean	Whether to apply XSS filtering
326
	 * @return	mixed
327
	 *
328
	 * @codeCoverageIgnore
329
	 */
330
	public function server($index, $xss_clean = NULL)
331
	{
332
		return $this->_fetch_from_array($_SERVER, $index, $xss_clean);
333
	}
334
335
	// ------------------------------------------------------------------------
336
337
	/**
338
	 * Fetch an item from the php://input stream
339
	 *
340
	 * Useful when you need to access PUT, DELETE or PATCH request data.
341
	 *
342
	 * @param	string	$index		Index for item to be fetched
343
	 * @param	bool	$xss_clean	Whether to apply XSS filtering
344
	 * @return	mixed
345
	 *
346
	 * @codeCoverageIgnore
347
	 */
348
	public function input_stream($index = NULL, $xss_clean = NULL)
349
	{
350
		// Prior to PHP 5.6, the input stream can only be read once,
351
		// so we'll need to check if we have already done that first.
352
		if ( ! is_array($this->_input_stream))
353
		{
354
			// $this->raw_input_stream will trigger __get().
355
			parse_str($this->raw_input_stream, $this->_input_stream);
0 ignored issues
show
Bug introduced by
The property raw_input_stream does not seem to exist. Did you mean _raw_input_stream?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
356
			is_array($this->_input_stream) OR $this->_input_stream = array();
357
		}
358
359
		return $this->_fetch_from_array($this->_input_stream, $index, $xss_clean);
360
	}
361
362
	// ------------------------------------------------------------------------
363
364
	/**
365
	 * Set cookie
366
	 *
367
	 * Accepts an arbitrary number of parameters (up to 7) or an associative
368
	 * array in the first parameter containing all the values.
369
	 *
370
	 * @param	string|mixed[]	$name		Cookie name or an array containing parameters
371
	 * @param	string		$value		Cookie value
372
	 * @param	int		$expire		Cookie expiration time in seconds
373
	 * @param	string		$domain		Cookie domain (e.g.: '.yourdomain.com')
374
	 * @param	string		$path		Cookie path (default: '/')
375
	 * @param	string		$prefix		Cookie name prefix
376
	 * @param	bool		$secure		Whether to only transfer cookies via SSL
377
	 * @param	bool		$httponly	Whether to only makes the cookie accessible via HTTP (no javascript)
378
	 * @return	void
379
	 *
380
	 * modified by ci-phpunit-test
381
	 */
382
	public function set_cookie($name, $value = '', $expire = '', $domain = '', $path = '/', $prefix = '', $secure = NULL, $httponly = NULL)
383
	{
384
		if (is_array($name))
385
		{
386
			// always leave 'name' in last place, as the loop will break otherwise, due to $$item
387
			foreach (array('value', 'expire', 'domain', 'path', 'prefix', 'secure', 'httponly', 'name') as $item)
388
			{
389
				if (isset($name[$item]))
390
				{
391
					$$item = $name[$item];
392
				}
393
			}
394
		}
395
396
		if ($prefix === '' && config_item('cookie_prefix') !== '')
397
		{
398
			$prefix = config_item('cookie_prefix');
399
		}
400
401
		if ($domain == '' && config_item('cookie_domain') != '')
402
		{
403
			$domain = config_item('cookie_domain');
404
		}
405
406
		if ($path === '/' && config_item('cookie_path') !== '/')
407
		{
408
			$path = config_item('cookie_path');
409
		}
410
411
		$secure = ($secure === NULL && config_item('cookie_secure') !== NULL)
412
			? (bool) config_item('cookie_secure')
413
			: (bool) $secure;
414
415
		$httponly = ($httponly === NULL && config_item('cookie_httponly') !== NULL)
416
			? (bool) config_item('cookie_httponly')
417
			: (bool) $httponly;
418
419
		if ( ! is_numeric($expire))
420
		{
421
			$expire = time() - 86500;
422
		}
423
		else
424
		{
425
			$expire = ($expire > 0) ? time() + $expire : 0;
426
		}
427
428
//		setcookie($prefix.$name, $value, $expire, $path, $domain, $secure, $httponly);
429
430
		// Save cookie in Output object
431
		// added by ci-phpunit-test
432
		$CI =& get_instance();
433
		$output = $CI->output;
434
		$output->_cookies[$prefix.$name][] = [
435
			'value' => $value,
436
			'expire' => $expire,
437
			'path' => $path,
438
			'domain' => $domain,
439
			'secure' => $secure,
440
			'httponly' => $httponly,
441
		];
442
	}
443
444
	// --------------------------------------------------------------------
445
446
	/**
447
	 * Fetch the IP Address
448
	 *
449
	 * Determines and validates the visitor's IP address.
450
	 *
451
	 * @return	string	IP address
452
	 *
453
	 * @codeCoverageIgnore
454
	 */
455
	public function ip_address()
456
	{
457
		if ($this->ip_address !== FALSE)
458
		{
459
			return $this->ip_address;
460
		}
461
462
		$proxy_ips = config_item('proxy_ips');
463
		if ( ! empty($proxy_ips) && ! is_array($proxy_ips))
464
		{
465
			$proxy_ips = explode(',', str_replace(' ', '', $proxy_ips));
466
		}
467
468
		$this->ip_address = $this->server('REMOTE_ADDR');
469
470
		if ($proxy_ips)
471
		{
472
			foreach (array('HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'HTTP_X_CLIENT_IP', 'HTTP_X_CLUSTER_CLIENT_IP') as $header)
473
			{
474
				if (($spoof = $this->server($header)) !== NULL)
475
				{
476
					// Some proxies typically list the whole chain of IP
477
					// addresses through which the client has reached us.
478
					// e.g. client_ip, proxy_ip1, proxy_ip2, etc.
479
					sscanf($spoof, '%[^,]', $spoof);
480
481
					if ( ! $this->valid_ip($spoof))
482
					{
483
						$spoof = NULL;
484
					}
485
					else
486
					{
487
						break;
488
					}
489
				}
490
			}
491
492
			if ($spoof)
493
			{
494
				for ($i = 0, $c = count($proxy_ips); $i < $c; $i++)
495
				{
496
					// Check if we have an IP address or a subnet
497
					if (strpos($proxy_ips[$i], '/') === FALSE)
498
					{
499
						// An IP address (and not a subnet) is specified.
500
						// We can compare right away.
501
						if ($proxy_ips[$i] === $this->ip_address)
502
						{
503
							$this->ip_address = $spoof;
0 ignored issues
show
Bug introduced by
The variable $spoof does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
504
							break;
505
						}
506
507
						continue;
508
					}
509
510
					// We have a subnet ... now the heavy lifting begins
511
					isset($separator) OR $separator = $this->valid_ip($this->ip_address, 'ipv6') ? ':' : '.';
512
513
					// If the proxy entry doesn't match the IP protocol - skip it
514
					if (strpos($proxy_ips[$i], $separator) === FALSE)
515
					{
516
						continue;
517
					}
518
519
					// Convert the REMOTE_ADDR IP address to binary, if needed
520
					if ( ! isset($ip, $sprintf))
521
					{
522
						if ($separator === ':')
523
						{
524
							// Make sure we're have the "full" IPv6 format
525
							$ip = explode(':',
526
								str_replace('::',
527
									str_repeat(':', 9 - substr_count($this->ip_address, ':')),
528
									$this->ip_address
529
								)
530
							);
531
532
							for ($j = 0; $j < 8; $j++)
533
							{
534
								$ip[$j] = intval($ip[$j], 16);
535
							}
536
537
							$sprintf = '%016b%016b%016b%016b%016b%016b%016b%016b';
538
						}
539
						else
540
						{
541
							$ip = explode('.', $this->ip_address);
542
							$sprintf = '%08b%08b%08b%08b';
543
						}
544
545
						$ip = vsprintf($sprintf, $ip);
546
					}
547
548
					// Split the netmask length off the network address
549
					sscanf($proxy_ips[$i], '%[^/]/%d', $netaddr, $masklen);
0 ignored issues
show
Bug introduced by
The variable $masklen does not exist. Did you forget to declare it?

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

Loading history...
550
551
					// Again, an IPv6 address is most likely in a compressed form
552
					if ($separator === ':')
553
					{
554
						$netaddr = explode(':', str_replace('::', str_repeat(':', 9 - substr_count($netaddr, ':')), $netaddr));
555
						for ($j = 0; $j < 8; $j++)
556
						{
557
							$netaddr[$j] = intval($netaddr[$j], 16);
558
						}
559
					}
560
					else
561
					{
562
						$netaddr = explode('.', $netaddr);
563
					}
564
565
					// Convert to binary and finally compare
566
					if (strncmp($ip, vsprintf($sprintf, $netaddr), $masklen) === 0)
567
					{
568
						$this->ip_address = $spoof;
569
						break;
570
					}
571
				}
572
			}
573
		}
574
575
		if ( ! $this->valid_ip($this->ip_address))
576
		{
577
			return $this->ip_address = '0.0.0.0';
578
		}
579
580
		return $this->ip_address;
581
	}
582
583
	// --------------------------------------------------------------------
584
585
	/**
586
	 * Validate IP Address
587
	 *
588
	 * @param	string	$ip	IP address
589
	 * @param	string	$which	IP protocol: 'ipv4' or 'ipv6'
590
	 * @return	bool
591
	 *
592
	 * @codeCoverageIgnore
593
	 */
594
	public function valid_ip($ip, $which = '')
595
	{
596
		switch (strtolower($which))
597
		{
598
			case 'ipv4':
599
				$which = FILTER_FLAG_IPV4;
600
				break;
601
			case 'ipv6':
602
				$which = FILTER_FLAG_IPV6;
603
				break;
604
			default:
605
				$which = NULL;
606
				break;
607
		}
608
609
		return (bool) filter_var($ip, FILTER_VALIDATE_IP, $which);
610
	}
611
612
	// --------------------------------------------------------------------
613
614
	/**
615
	 * Fetch User Agent string
616
	 *
617
	 * @return	string|null	User Agent string or NULL if it doesn't exist
618
	 *
619
	 * @codeCoverageIgnore
620
	 */
621
	public function user_agent($xss_clean = NULL)
622
	{
623
		return $this->_fetch_from_array($_SERVER, 'HTTP_USER_AGENT', $xss_clean);
624
	}
625
626
	// --------------------------------------------------------------------
627
628
	/**
629
	 * Sanitize Globals
630
	 *
631
	 * Internal method serving for the following purposes:
632
	 *
633
	 *	- Unsets $_GET data, if query strings are not enabled
634
	 *	- Cleans POST, COOKIE and SERVER data
635
	 * 	- Standardizes newline characters to PHP_EOL
636
	 *
637
	 * @return	void
638
	 *
639
	 * @codeCoverageIgnore
640
	 */
641
	protected function _sanitize_globals()
642
	{
643
		// Is $_GET data allowed? If not we'll set the $_GET to an empty array
644
		if ($this->_allow_get_array === FALSE)
645
		{
646
			$_GET = array();
647
		}
648
		elseif (is_array($_GET))
649
		{
650
			foreach ($_GET as $key => $val)
651
			{
652
				$_GET[$this->_clean_input_keys($key)] = $this->_clean_input_data($val);
653
			}
654
		}
655
656
		// Clean $_POST Data
657
		if (is_array($_POST))
658
		{
659
			foreach ($_POST as $key => $val)
660
			{
661
				$_POST[$this->_clean_input_keys($key)] = $this->_clean_input_data($val);
662
			}
663
		}
664
665
		// Clean $_COOKIE Data
666
		if (is_array($_COOKIE))
667
		{
668
			// Also get rid of specially treated cookies that might be set by a server
669
			// or silly application, that are of no use to a CI application anyway
670
			// but that when present will trip our 'Disallowed Key Characters' alarm
671
			// http://www.ietf.org/rfc/rfc2109.txt
672
			// note that the key names below are single quoted strings, and are not PHP variables
673
			unset(
674
				$_COOKIE['$Version'],
675
				$_COOKIE['$Path'],
676
				$_COOKIE['$Domain']
677
			);
678
679
			foreach ($_COOKIE as $key => $val)
680
			{
681
				if (($cookie_key = $this->_clean_input_keys($key)) !== FALSE)
682
				{
683
					$_COOKIE[$cookie_key] = $this->_clean_input_data($val);
684
				}
685
				else
686
				{
687
					unset($_COOKIE[$key]);
688
				}
689
			}
690
		}
691
692
		// Sanitize PHP_SELF
693
		$_SERVER['PHP_SELF'] = strip_tags($_SERVER['PHP_SELF']);
694
695
		log_message('debug', 'Global POST, GET and COOKIE data sanitized');
696
	}
697
698
	// --------------------------------------------------------------------
699
700
	/**
701
	 * Clean Input Data
702
	 *
703
	 * Internal method that aids in escaping data and
704
	 * standardizing newline characters to PHP_EOL.
705
	 *
706
	 * @param	string|string[]	$str	Input string(s)
707
	 * @return	string
708
	 *
709
	 * @codeCoverageIgnore
710
	 */
711
	protected function _clean_input_data($str)
712
	{
713
		if (is_array($str))
714
		{
715
			$new_array = array();
716
			foreach (array_keys($str) as $key)
717
			{
718
				$new_array[$this->_clean_input_keys($key)] = $this->_clean_input_data($str[$key]);
719
			}
720
			return $new_array;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $new_array; (array) is incompatible with the return type documented by CI_Input::_clean_input_data 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...
721
		}
722
723
		/* We strip slashes if magic quotes is on to keep things consistent
724
725
		   NOTE: In PHP 5.4 get_magic_quotes_gpc() will always return 0 and
726
		         it will probably not exist in future versions at all.
727
		*/
728
		if ( ! is_php('5.4') && get_magic_quotes_gpc())
729
		{
730
			$str = stripslashes($str);
731
		}
732
733
		// Clean UTF-8 if supported
734
		if (UTF8_ENABLED === TRUE)
735
		{
736
			$str = $this->uni->clean_string($str);
737
		}
738
739
		// Remove control characters
740
		$str = remove_invisible_characters($str, FALSE);
741
742
		// Standardize newlines if needed
743
		if ($this->_standardize_newlines === TRUE)
744
		{
745
			return preg_replace('/(?:\r\n|[\r\n])/', PHP_EOL, $str);
746
		}
747
748
		return $str;
749
	}
750
751
	// --------------------------------------------------------------------
752
753
	/**
754
	 * Clean Keys
755
	 *
756
	 * Internal method that helps to prevent malicious users
757
	 * from trying to exploit keys we make sure that keys are
758
	 * only named with alpha-numeric text and a few other items.
759
	 *
760
	 * @param	string	$str	Input string
761
	 * @param	bool	$fatal	Whether to terminate script exection
762
	 *				or to return FALSE if an invalid
763
	 *				key is encountered
764
	 * @return	string|bool
765
	 *
766
	 * @codeCoverageIgnore
767
	 */
768
	protected function _clean_input_keys($str, $fatal = TRUE)
769
	{
770
		if ( ! preg_match('/^[a-z0-9:_\/|-]+$/i', $str))
771
		{
772
			if ($fatal === TRUE)
773
			{
774
				return FALSE;
775
			}
776
			else
777
			{
778
				set_status_header(503);
779
				echo 'Disallowed Key Characters.';
780
				exit(7); // EXIT_USER_INPUT
781
			}
782
		}
783
784
		// Clean UTF-8 if supported
785
		if (UTF8_ENABLED === TRUE)
786
		{
787
			return $this->uni->clean_string($str);
788
		}
789
790
		return $str;
791
	}
792
793
	// --------------------------------------------------------------------
794
795
	/**
796
	 * Request Headers
797
	 *
798
	 * @param	bool	$xss_clean	Whether to apply XSS filtering
799
	 * @return	array
800
	 *
801
	 * @codeCoverageIgnore
802
	 */
803
	public function request_headers($xss_clean = FALSE)
804
	{
805
		// If header is already defined, return it immediately
806
		if ( ! empty($this->headers))
807
		{
808
			return $this->_fetch_from_array($this->headers, NULL, $xss_clean);
809
		}
810
811
		// In Apache, you can simply call apache_request_headers()
812
		if (function_exists('apache_request_headers'))
813
		{
814
			$this->headers = apache_request_headers();
815
		}
816
		else
817
		{
818
			isset($_SERVER['CONTENT_TYPE']) && $this->headers['Content-Type'] = $_SERVER['CONTENT_TYPE'];
819
820
			foreach ($_SERVER as $key => $val)
821
			{
822
				if (sscanf($key, 'HTTP_%s', $header) === 1)
823
				{
824
					// take SOME_HEADER and turn it into Some-Header
825
					$header = str_replace('_', ' ', strtolower($header));
826
					$header = str_replace(' ', '-', ucwords($header));
827
828
					$this->headers[$header] = $_SERVER[$key];
829
				}
830
			}
831
		}
832
833
		return $this->_fetch_from_array($this->headers, NULL, $xss_clean);
834
	}
835
836
	// --------------------------------------------------------------------
837
838
	/**
839
	 * Get Request Header
840
	 *
841
	 * Returns the value of a single member of the headers class member
842
	 *
843
	 * @param	string		$index		Header name
844
	 * @param	bool		$xss_clean	Whether to apply XSS filtering
845
	 * @return	string|null	The requested header on success or NULL on failure
846
	 *
847
	 * modified by ci-phpunit-test
848
	 */
849
	public function get_request_header($index, $xss_clean = FALSE)
850
	{
851
//		static $headers;
852
853
		if ( ! isset($headers))
0 ignored issues
show
Bug introduced by
The variable $headers seems only to be defined at a later point. As such the call to isset() seems to always evaluate to false.

This check marks calls to isset(...) or empty(...) that are found before the variable itself is defined. These will always have the same result.

This is likely the result of code being shifted around. Consider removing these calls.

Loading history...
854
		{
855
			empty($this->headers) && $this->request_headers();
856
			foreach ($this->headers as $key => $value)
857
			{
858
				$headers[strtolower($key)] = $value;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$headers was never initialized. Although not strictly required by PHP, it is generally a good practice to add $headers = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
859
			}
860
		}
861
862
		$index = strtolower($index);
863
864
		if ( ! isset($headers[$index]))
865
		{
866
			return NULL;
867
		}
868
869
		return ($xss_clean === TRUE)
870
			? $this->security->xss_clean($headers[$index])
871
			: $headers[$index];
872
	}
873
874
	// --------------------------------------------------------------------
875
876
	/**
877
	 * Is AJAX request?
878
	 *
879
	 * Test to see if a request contains the HTTP_X_REQUESTED_WITH header.
880
	 *
881
	 * @return 	bool
882
	 *
883
	 * @codeCoverageIgnore
884
	 */
885
	public function is_ajax_request()
886
	{
887
		return ( ! empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest');
888
	}
889
890
	// --------------------------------------------------------------------
891
892
	/**
893
	 * Is CLI request?
894
	 *
895
	 * Test to see if a request was made from the command line.
896
	 *
897
	 * @deprecated	3.0.0	Use is_cli() instead
898
	 * @return	bool
899
	 *
900
	 * @codeCoverageIgnore
901
	 */
902
	public function is_cli_request()
903
	{
904
		return is_cli();
905
	}
906
907
	// --------------------------------------------------------------------
908
909
	/**
910
	 * Get Request Method
911
	 *
912
	 * Return the request method
913
	 *
914
	 * @param	bool	$upper	Whether to return in upper or lower case
915
	 *				(default: FALSE)
916
	 * @return 	string
917
	 *
918
	 * @codeCoverageIgnore
919
	 */
920
	public function method($upper = FALSE)
921
	{
922
		return ($upper)
923
			? strtoupper($this->server('REQUEST_METHOD'))
924
			: strtolower($this->server('REQUEST_METHOD'));
925
	}
926
927
	// ------------------------------------------------------------------------
928
929
	/**
930
	 * Magic __get()
931
	 *
932
	 * Allows read access to protected properties
933
	 *
934
	 * @param	string	$name
935
	 * @return	mixed
936
	 *
937
	 * @codeCoverageIgnore
938
	 */
939
	public function __get($name)
940
	{
941
		if ($name === 'raw_input_stream')
942
		{
943
			isset($this->_raw_input_stream) OR $this->_raw_input_stream = file_get_contents('php://input');
944
			return $this->_raw_input_stream;
945
		}
946
		elseif ($name === 'ip_address')
947
		{
948
			return $this->ip_address;
949
		}
950
	}
951
952
}
953