欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  php教程

CI框架源码阅读---------Input.php

程序员文章站 2024-01-19 22:53:58
...
[php]

/**

* CodeIgniter

*

* An open source application development framework for PHP 5.1.6 or newer

*

* @package CodeIgniter

* @author ExpressionEngine Dev Team

* @copyright Copyright (c) 2008 - 2011, EllisLab, Inc.

* @license http://codeigniter.com/user_guide/license.html

* @link http://codeigniter.com

* @since Version 1.0

* @filesource

*/

// ------------------------------------------------------------------------

/**

* Input Class

*

* Pre-processes global input data for security

*

* @package CodeIgniter

* @subpackage Libraries

* @category Input

* @author ExpressionEngine Dev Team

* @link http://codeigniter.com/user_guide/libraries/input.html

*/

class CI_Input {

/**

* IP address of the current user

* 当前用户的ip地址

* @var string

*/

var $ip_address = FALSE;

/**

* user agent (web browser) being used by the current user

* 当前用户(web浏览器)代理

* @var string

*/

var $user_agent = FALSE;

/**

* If FALSE, then $_GET will be set to an empty array

* 如果是FALSE , $_GET将被设置为空数组

* @var bool

*/

var $_allow_get_array = TRUE;

/**

* If TRUE, then newlines are standardized

* 如果为TRUR,新行将被标准化

*

* @var bool

*/

var $_standardize_newlines = TRUE;

/**

* Determines whether the XSS filter is always active when GET, POST or COOKIE data is encountered

* Set automatically based on config setting

* 决定是否总是在GET ,POST , COOKIE数据中进行XSS过滤

* 在配置选项里面配置是否自动开启

*

* @var bool

*/

var $_enable_xss = FALSE;

/**

* Enables a CSRF cookie token to be set.

* Set automatically based on config setting

* 允许CSRF cookie令牌

*

* @var bool

*/

var $_enable_csrf = FALSE;

/**

* List of all HTTP request headers

* HTTP请求头部的列表

* @var array

*/

protected $headers = array();

/**

* Constructor

* 设置是否全局允许XSS处理和是否允许使用$_GET数组

* Sets whether to globally enable the XSS processing

* and whether to allow the $_GET array

*

* @return void

*/

public function __construct()

{

log_message('debug', "Input Class Initialized");

// 从配置文件中获取是否进行全局允许使用$_GET XSS过滤和csrf保护

$this->_allow_get_array = (config_item('allow_get_array') === TRUE);

$this->_enable_xss = (config_item('global_xss_filtering') === TRUE);

$this->_enable_csrf = (config_item('csrf_protection') === TRUE);

// 清除globals变量,在开启了globals_register的情况下,相当于关闭了此配置。

// 开启一道 安全防护

global $SEC;

$this->security =& $SEC;

// Do we need the UTF-8 class?

if (UTF8_ENABLED === TRUE)

{

global $UNI;

$this->uni =& $UNI;

}

// Sanitize global arrays

$this->_sanitize_globals();

}

// --------------------------------------------------------------------

/**

* Fetch from array

* 从$array获取值,如果设置了xss_clean 那么进行过滤

* This is a helper function to retrieve 检索 values from global arrays

* 这是一个帮助函数用来从全局数组中检索

*

* @access private

* @param array

* @param string

* @param bool

* @return string

*/

function _fetch_from_array(&$array, $index = '', $xss_clean = FALSE)

{

if ( ! isset($array[$index]))

{

return FALSE;

}

if ($xss_clean === TRUE)

{

return $this->security->xss_clean($array[$index]);

}

return $array[$index];

}

// --------------------------------------------------------------------

/**

* Fetch an item from the GET array

* 获取过滤后的GET数组

* @access public

* @param string

* @param bool

* @return string

*/

function get($index = NULL, $xss_clean = FALSE)

{

// Check if a field has been provided

// 检查是否一个字段已经被提供

if ($index === NULL AND ! emptyempty($_GET))

{

$get = array();

// loop through the full _GET array

// 遍历_GET数组

foreach (array_keys($_GET) as $key)

{

$get[$key] = $this->_fetch_from_array($_GET, $key, $xss_clean);

}

return $get;

}

return $this->_fetch_from_array($_GET, $index, $xss_clean);

}

// --------------------------------------------------------------------

/**

* Fetch an item from the POST array

* 获取过滤后的$_POST值

* @access public

* @param string

* @param bool

* @return string

*/

function post($index = NULL, $xss_clean = FALSE)

{

// Check if a field has been provided

if ($index === NULL AND ! emptyempty($_POST))

{

$post = array();

// Loop through the full _POST array and return it

foreach (array_keys($_POST) as $key)

{

$post[$key] = $this->_fetch_from_array($_POST, $key, $xss_clean);

}

return $post;

}

return $this->_fetch_from_array($_POST, $index, $xss_clean);

}

// --------------------------------------------------------------------

/**

* Fetch an item from either the GET array or the POST

* 从get和post中获取值, post优先

* @access public

* @param string The index key

* @param bool XSS cleaning

* @return string

*/

function get_post($index = '', $xss_clean = FALSE)

{

if ( ! isset($_POST[$index]) )

{

return $this->get($index, $xss_clean);

}

else

{

return $this->post($index, $xss_clean);

}

}

// --------------------------------------------------------------------

/**

* Fetch an item from the COOKIE array

* 返回过滤后的COOKIE值

* @access public

* @param string

* @param bool

* @return string

*/

function cookie($index = '', $xss_clean = FALSE)

{

return $this->_fetch_from_array($_COOKIE, $index, $xss_clean);

}

// ------------------------------------------------------------------------

/**

* Set cookie

*

* Accepts six parameter, or you can submit an associative

* array in the first parameter containing all the values.

* 接收6个参数或者接收一个关联数组里面包含所有的值

* @access public

* @param mixed

* @param string the value of the cookie

* @param string the number of seconds until expiration

* @param string the cookie domain. Usually: .yourdomain.com

* @param string the cookie path

* @param string the cookie prefix

* @param bool true makes the cookie secure

* @return void

*/

function set_cookie($name = '', $value = '', $expire = '', $domain = '', $path = '/', $prefix = '', $secure = FALSE)

{

// 如果第一个值是数组 将数组中的值分别赋值给留个参数

if (is_array($name))

{

// always leave 'name' in last place, as the loop will break otherwise, due to $$item

foreach (array('value', 'expire', 'domain', 'path', 'prefix', 'secure', 'name') as $item)

{

if (isset($name[$item]))

{

$$item = $name[$item];

}

}

}

// 如果某个参数为默认值但是config.php中的配置不是默认值

// 则使用config.php中的配置值

if ($prefix == '' AND config_item('cookie_prefix') != '')

{

$prefix = config_item('cookie_prefix');

}

if ($domain == '' AND config_item('cookie_domain') != '')

{

$domain = config_item('cookie_domain');

}

if ($path == '/' AND config_item('cookie_path') != '/')

{

$path = config_item('cookie_path');

}

if ($secure == FALSE AND config_item('cookie_secure') != FALSE)

{

$secure = config_item('cookie_secure');

}

if ( ! is_numeric($expire))

{

$expire = time() - 86500;

}

else

{

$expire = ($expire > 0) ? time() + $expire : 0;

}

setcookie($prefix.$name, $value, $expire, $path, $domain, $secure);

}

// --------------------------------------------------------------------

/**

* Fetch an item from the SERVER array

* 返回过滤后的$_SERVER值

* @access public

* @param string

* @param bool

* @return string

*/

function server($index = '', $xss_clean = FALSE)

{

return $this->_fetch_from_array($_SERVER, $index, $xss_clean);

}

// --------------------------------------------------------------------

/**

* Fetch the IP Address

* 返回当前用户的IP。如果IP地址无效,返回0.0.0.0的IP:

* @return string

*/

public function ip_address()

{

// 如果已经有了ip_address 则返回

if ($this->ip_address !== FALSE)

{

return $this->ip_address;

}

$proxy_ips = config_item('proxy_ips');

if ( ! emptyempty($proxy_ips))

{

$proxy_ips = explode(',', str_replace(' ', '', $proxy_ips));

foreach (array('HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'HTTP_X_CLIENT_IP', 'HTTP_X_CLUSTER_CLIENT_IP') as $header)

{

if (($spoof = $this->server($header)) !== FALSE)

{

// Some proxies typically list the whole chain of IP

// addresses through which the client has reached us.

// e.g. client_ip, proxy_ip1, proxy_ip2, etc.

if (strpos($spoof, ',') !== FALSE)

{

$spoof = explode(',', $spoof, 2);

$spoof = $spoof[0];

}

if ( ! $this->valid_ip($spoof))

{

$spoof = FALSE;

}

else

{

break;

}

}

}

$this->ip_address = ($spoof !== FALSE && in_array($_SERVER['REMOTE_ADDR'], $proxy_ips, TRUE))

? $spoof : $_SERVER['REMOTE_ADDR'];

}

else

{

$this->ip_address = $_SERVER['REMOTE_ADDR'];

}

if ( ! $this->valid_ip($this->ip_address))

{

$this->ip_address = '0.0.0.0';

}

return $this->ip_address;

}

// --------------------------------------------------------------------

/**

* Validate IP Address

* 测试输入的IP地址是不是有效,返回布尔值TRUE或者FALSE。

* 注意:$this->input->ip_address()自动测试输入的IP地址本身格式是不是有效。

* @access public

* @param string

* @param string ipv4 or ipv6

* @return bool

*/

public function valid_ip($ip, $which = '')

{

$which = strtolower($which);

// First check if filter_var is available

if (is_callable('filter_var'))

{

switch ($which) {

case 'ipv4':

$flag = FILTER_FLAG_IPV4;

break;

case 'ipv6':

$flag = FILTER_FLAG_IPV6;

break;

default:

$flag = '';

break;

}

return (bool) filter_var($ip, FILTER_VALIDATE_IP, $flag);

}

if ($which !== 'ipv6' && $which !== 'ipv4')

{

if (strpos($ip, ':') !== FALSE)

{

$which = 'ipv6';

}

elseif (strpos($ip, '.') !== FALSE)

{

$which = 'ipv4';

}

else

{

return FALSE;

}

}

$func = '_valid_'.$which;

return $this->$func($ip);

}

// --------------------------------------------------------------------

/**

* Validate IPv4 Address

* 验证ipv4地址

* Updated version suggested by Geert De Deckere

*

* @access protected

* @param string

* @return bool

*/

protected function _valid_ipv4($ip)

{

$ip_segments = explode('.', $ip);

// Always 4 segments needed

if (count($ip_segments) !== 4)

{

return FALSE;

}

// IP can not start with 0

if ($ip_segments[0][0] == '0')

{

return FALSE;

}

// Check each segment

foreach ($ip_segments as $segment)

{

// IP segments must be digits and can not be

// longer than 3 digits or greater then 255

if ($segment == '' OR preg_match("/[^0-9]/", $segment) OR $segment > 255 OR strlen($segment) > 3)

{

return FALSE;

}

}

return TRUE;

}

// --------------------------------------------------------------------

/**

* Validate IPv6 Address

* 验证ipv6地址

* @access protected

* @param string

* @return bool

*/

protected function _valid_ipv6($str)

{

// 8 groups, separated by :

// 0-ffff per group

// one set of consecutive 0 groups can be collapsed to ::

$groups = 8;

$collapsed = FALSE;

$chunks = array_filter(

preg_split('/(:{1,2})/', $str, NULL, PREG_SPLIT_DELIM_CAPTURE)

);

// Rule out easy nonsense

if (current($chunks) == ':' OR end($chunks) == ':')

{

return FALSE;

}

// PHP supports IPv4-mapped IPv6 addresses, so we'll expect those as well

if (strpos(end($chunks), '.') !== FALSE)

{

$ipv4 = array_pop($chunks);

if ( ! $this->_valid_ipv4($ipv4))

{

return FALSE;

}

$groups--;

}

while ($seg = array_pop($chunks))

{

if ($seg[0] == ':')

{

if (--$groups == 0)

{

return FALSE; // too many groups

}

if (strlen($seg) > 2)

{

return FALSE; // long separator

}

if ($seg == '::')

{

if ($collapsed)

{

return FALSE; // multiple collapsed

}

$collapsed = TRUE;

}

}

elseif (preg_match("/[^0-9a-f]/i", $seg) OR strlen($seg) > 4)

{

return FALSE; // invalid segment

}

}

return $collapsed OR $groups == 1;

}

// --------------------------------------------------------------------

/**

* User Agent

* 返回当前用户正在使用的浏览器的user agent信息。 如果不能得到数据,返回FALSE。

* 一般user_agent为空的时候被认定为手机访问,或者curl的抓取,或则会蜘蛛抓取

* @access public

* @return string

*/

function user_agent()

{

if ($this->user_agent !== FALSE)

{

return $this->user_agent;

}

$this->user_agent = ( ! isset($_SERVER['HTTP_USER_AGENT'])) ? FALSE : $_SERVER['HTTP_USER_AGENT'];

return $this->user_agent;

}

// --------------------------------------------------------------------

/**

* Sanitize Globals

* 清理全局数组

* This function does the following:

* 这个函数做了下面的操作:

* Unsets $_GET data (if query strings are not enabled)

* 销毁$_GET (如果query strings 没有开启)

* Unsets all globals if register_globals is enabled

* 销毁所有全局数组如果register_globals开启

*

* Standardizes newline characters to \n

* 标准化换行符\n

* @access private

* @return void

*/

function _sanitize_globals()

{

// It would be "wrong" to unset any of these GLOBALS.

// 销毁下面的全局数组将是错误的。

$protected = array('_SERVER', '_GET', '_POST', '_FILES', '_REQUEST',

'_SESSION', '_ENV', 'GLOBALS', 'HTTP_RAW_POST_DATA',

'system_folder', 'application_folder', 'BM', 'EXT',

'CFG', 'URI', 'RTR', 'OUT', 'IN');

// Unset globals for securiy.为了安全销毁除了上面之外的全局数组

// This is effectively the same as register_globals = off

// 这样的效果和register_globals是相同的

// 经过下面处理后,所有的非保护的全局变量将被删除掉

foreach (array($_GET, $_POST, $_COOKIE) as $global)

{

if ( ! is_array($global))

{

if ( ! in_array($global, $protected))

{

global $$global;

$$global = NULL;

}

}

else

{

foreach ($global as $key => $val)

{

if ( ! in_array($key, $protected))

{

global $$key;

$$key = NULL;

}

}

}

}

// Is $_GET data allowed? If not we'll set the $_GET to an empty array

// 是否允许$_GET数据? 如果不允许的话,设置$_GET为空数组

if ($this->_allow_get_array == FALSE)

{

$_GET = array();

}

else

{

if (is_array($_GET) AND count($_GET) > 0)

{

foreach ($_GET as $key => $val)

{

$_GET[$this->_clean_input_keys($key)] = $this->_clean_input_data($val);

}

}

}

// Clean $_POST Data

// 过滤$_POST 数组

if (is_array($_POST) AND count($_POST) > 0)

{

foreach ($_POST as $key => $val)

{

$_POST[$this->_clean_input_keys($key)] = $this->_clean_input_data($val);

}

}

// Clean $_COOKIE Data

// 过滤$_COOKIE数组

if (is_array($_COOKIE) AND count($_COOKIE) > 0)

{

// Also get rid of specially treated cookies that might be set by a server

// or silly application, that are of no use to a CI application anyway

// but that when present will trip our 'Disallowed Key Characters' alarm

// http://www.ietf.org/rfc/rfc2109.txt

// note that the key names below are single quoted strings, and are not PHP variables

unset($_COOKIE['$Version']);

unset($_COOKIE['$Path']);

unset($_COOKIE['$Domain']);

foreach ($_COOKIE as $key => $val)

{

$_COOKIE[$this->_clean_input_keys($key)] = $this->_clean_input_data($val);

}

}

// Sanitize PHP_SELF

$_SERVER['PHP_SELF'] = strip_tags($_SERVER['PHP_SELF']);

// CSRF Protection check on HTTP requests

// CSRF保护检测http请求

if ($this->_enable_csrf == TRUE && ! $this->is_cli_request())

{

$this->security->csrf_verify();

}

log_message('debug', "Global POST and COOKIE data sanitized");

}

// --------------------------------------------------------------------

/**

* Clean Input Data

* 过滤input数据

* This is a helper function. It escapes data and

* standardizes newline characters to \n

*

* @access private

* @param string

* @return string

*/

function _clean_input_data($str)

{

if (is_array($str))

{

$new_array = array();

foreach ($str as $key => $val)

{

$new_array[$this->_clean_input_keys($key)] = $this->_clean_input_data($val);

}

return $new_array;

}

/* We strip slashes if magic quotes is on to keep things consistent

如果小于PHP5.4版本,并且get_magic_quotes_gpc开启了,则去掉斜线。

NOTE: In PHP 5.4 get_magic_quotes_gpc() will always return 0 and

it will probably not exist in future versions at all。

注意:在PHP5.4及之后版本,get_magic_quotes_gpc()将总是返回0,

在后续版本中可能会移除该特性

*/

if ( ! is_php('5.4') && get_magic_quotes_gpc())

{

$str = stripslashes($str);

}

// Clean UTF-8 if supported 如果支持清理utf8

if (UTF8_ENABLED === TRUE)

{

$str = $this->uni->clean_string($str);

}

// Remove control characters

$str = remove_invisible_characters($str);

// Should we filter the input data?

if ($this->_enable_xss === TRUE)

{

$str = $this->security->xss_clean($str);

}

// Standardize newlines if needed

if ($this->_standardize_newlines == TRUE)

{

if (strpos($str, "\r") !== FALSE)

{

$str = str_replace(array("\r\n", "\r", "\r\n\n"), PHP_EOL, $str);

}

}

return $str;

}

// --------------------------------------------------------------------

/**

* Clean Keys

* 过滤键值

* This is a helper function. To prevent malicious users

* from trying to exploit keys we make sure that keys are

* only named with alpha-numeric text and a few other items.

*

* @access private

* @param string

* @return string

*/

function _clean_input_keys($str)

{

if ( ! preg_match("/^[a-z0-9:_\/-]+$/i", $str))

{

exit('Disallowed Key Characters.');

}

// Clean UTF-8 if supported

if (UTF8_ENABLED === TRUE)

{

$str = $this->uni->clean_string($str);

}

return $str;

}

// --------------------------------------------------------------------

/**

* Request Headers

* 返回请求头(header)数组。

* In Apache, you can simply call apache_request_headers(), however for

* people running other webservers the function is undefined.

*

* @param bool XSS cleaning

*

* @return array

*/

public function request_headers($xss_clean = FALSE)

{

// Look at Apache go!

if (function_exists('apache_request_headers'))

{

$headers = apache_request_headers();

}

else

{

$headers['Content-Type'] = (isset($_SERVER['CONTENT_TYPE'])) ? $_SERVER['CONTENT_TYPE'] : @getenv('CONTENT_TYPE');

foreach ($_SERVER as $key => $val)

{

if (strncmp($key, 'HTTP_', 5) === 0)

{

$headers[substr($key, 5)] = $this->_fetch_from_array($_SERVER, $key, $xss_clean);

}

}

}

// take SOME_HEADER and turn it into Some-Header

foreach ($headers as $key => $val)

{

$key = str_replace('_', ' ', strtolower($key));

$key = str_replace(' ', '-', ucwords($key));

$this->headers[$key] = $val;

}

return $this->headers;

}

// --------------------------------------------------------------------

/**

* Get Request Header

* 返回请求头(request header)数组中某一个元素的值

* Returns the value of a single member of the headers class member

*

* @param string array key for $this->headers

* @param boolean XSS Clean or not

* @return mixed FALSE on failure, string on success

*/

public function get_request_header($index, $xss_clean = FALSE)

{

if (emptyempty($this->headers))

{

$this->request_headers();

}

if ( ! isset($this->headers[$index]))

{

return FALSE;

}

if ($xss_clean === TRUE)

{

return $this->security->xss_clean($this->headers[$index]);

}

return $this->headers[$index];

}

// --------------------------------------------------------------------

/**

* Is ajax Request?

* 判断是否为ajax请求

* Test to see if a request contains the HTTP_X_REQUESTED_WITH header

*

* @return boolean

*/

public function is_ajax_request()

{

return ($this->server('HTTP_X_REQUESTED_WITH') === 'XMLHttpRequest');

}

// --------------------------------------------------------------------

/**

* Is cli Request?

* 判断是否来自cli请求

* Test to see if a request was made from the command line

*

* @return bool

*/

public function is_cli_request()

{

return (php_sapi_name() === 'cli' OR defined('STDIN'));

}

}

/* End of file Input.php */

/* Location: ./system/core/Input.php */