当前位置:首页 > PHP教程 > php应用 > 列表

CI框架Session.php源码分析

发布:smiling 来源: PHP粉丝网  添加日期:2021-04-22 10:04:40 浏览: 评论:0 

Session类并不使用PHP本身的session,而是使用类自己的session,这样做,可以给开发者提供更大的弹性。下面我们就来仔细分析下CI框架的Session类Session.php文件

CI的Session并不是原生的session,正是我前面所有的cookie based session,另外,CI可以根据用户选择配置是否将session存入数据库中,本人很喜欢这个功能,还有就是“闪出数据”的功能,既闪出数据只是对下次服务器请求可以,之后就会被自动清除。常见使用方法有:

  1. $this->session->set_userdata('some_name''some_value'); //设置session数据 
  2.  
  3. $this->session->userdata('item'); //获取session数据 
  4.  
  5. $this->session->unset_userdata('some_name'); //删除session数据 
  6.  
  7. $this->session->sess_destroy(); //销毁session数据 
  8.  
  9. $this->session->set_flashdata('item''value'); //设置闪存数据 
  10.  
  11. $this->session->flashdata('item'); //获取闪存数据 
  12.  
  13. $this->session->keep_flashdata('item');  //保留闪存数据  
  14.  
  15. /** 
  16.  * CI是 session based cookie 
  17.  */ 
  18. class CI_Session { 
  19.  var $sess_encrypt_cookie  = FALSE;  //是否对session加密 
  20.  var $sess_use_database   = FALSE; //是否将session存入数据库 
  21.  var $sess_table_name   = ''//session存入数据的表名 
  22.  var $sess_expiration   = 7200; //session的过期时间 
  23.  var $sess_expire_on_close  = FALSE; //当浏览器窗口关闭时是否自动使session过期 
  24.  var $sess_match_ip    = FALSE;//是否通过用户的IP地址来读取 session 的数据 
  25.  var $sess_match_useragent  = TRUE; //是否要按照对应的 User Agent 来读取 session 数据。 
  26.  var $sess_cookie_name   = 'ci_session'//cookie名称 
  27.  var $cookie_prefix    = ''//cookie前缀 
  28.  var $cookie_path    = ''//cookie路径 
  29.  var $cookie_domain    = ''//cookie作用域 
  30.  var $cookie_secure    = FALSE; //是否在安全的https协议下才有效 
  31.  var $sess_time_to_update  = 300; //session cookie多久更新一次 
  32.  var $encryption_key    = ''//加密key 
  33.  var $flashdata_key    = 'flash'
  34.  var $time_reference    = 'time'
  35.  var $gc_probability    = 5;  //回收session的能力 
  36.  var $userdata     = array(); //用户session数据保存变量 
  37.  var $CI//CI超级句柄 
  38.  var $now;  //当前时间 
  39.  public function __construct($params = array()) 
  40.  { 
  41.   log_message('debug'"Session Class Initialized"); 
  42.   // 获取CI超级类 
  43.   $this->CI =& get_instance(); 
  44.   // 获取config文件中的配置数据 
  45.   foreach (array('sess_encrypt_cookie''sess_use_database''sess_table_name''sess_expiration''sess_expire_on_close''sess_match_ip''sess_match_useragent''sess_cookie_name''cookie_path''cookie_domain''cookie_secure''sess_time_to_update''time_reference''cookie_prefix''encryption_key'as $key
  46.   { 
  47.    $this->$key = (isset($params[$key])) ? $params[$key] : $this->CI->config->item($key); 
  48.   } 
  49.                 //必须设置encryption_key 
  50.   if ($this->encryption_key == ''
  51.   { 
  52.    show_error('In order to use the Session class you are required to set an encryption key in your config file.'); 
  53.   } 
  54.   // 加载string helper函数 
  55.   $this->CI->load->helper('string'); 
  56.   // D如果对cookie进行加密,则引入加密类 
  57.   if ($this->sess_encrypt_cookie == TRUE) 
  58.   { 
  59.    $this->CI->load->library('encrypt'); 
  60.   } 
  61.   // 如果session计入数据库,则引入db 
  62.   if ($this->sess_use_database === TRUE AND $this->sess_table_name != ''
  63.   { 
  64.    $this->CI->load->database(); 
  65.   } 
  66.   // 获取当前时间 
  67.   $this->now = $this->_get_time(); 
  68.   // 如果没有设置session的有效时间,默认两年 
  69.   if ($this->sess_expiration == 0) 
  70.   { 
  71.    $this->sess_expiration = (60*60*24*365*2); 
  72.   } 
  73.   // 获取cookie名称 
  74.   $this->sess_cookie_name = $this->cookie_prefix.$this->sess_cookie_name; 
  75.   // 如果session不存在,创建新会话 
  76.   if ( ! $this->sess_read()) 
  77.   { 
  78.    $this->sess_create(); 
  79.   } 
  80.   else 
  81.   { 
  82.    $this->sess_update(); 
  83.   } 
  84.   // 闪出标记old的闪出数据 
  85.   $this->_flashdata_sweep(); 
  86.                 //将新的闪出数据标记成old 标记成old的数据的数据将会在下次请求时闪出 
  87.   $this->_flashdata_mark(); 
  88.   //回收/删除 过期的session 
  89.   $this->_sess_gc(); 
  90.   log_message('debug'"Session routines successfully run"); 
  91.  } 
  92.  // -------------------------------------------------------------------- 
  93.  /** 
  94.   * 读取session数据 
  95.   */ 
  96.  function sess_read() 
  97.  { 
  98.   // 获取session 
  99.   $session = $this->CI->input->cookie($this->sess_cookie_name); 
  100.   // 没有session  拜拜 
  101.   if ($session === FALSE) 
  102.   { 
  103.    log_message('debug''A session cookie was not found.'); 
  104.    return FALSE; 
  105.   } 
  106.   // 如果加密了cookie 
  107.   if ($this->sess_encrypt_cookie == TRUE) 
  108.   { 
  109.    $session = $this->CI->encrypt->decode($session); 
  110.   } 
  111.   else 
  112.   { 
  113.    // encryption was not used, so we need to check the md5 hash 
  114.    $hash  = substr($sessionstrlen($session)-32); // get last 32 chars 
  115.    $session = substr($session, 0, strlen($session)-32); 
  116.    // Does the md5 hash match?  This is to prevent manipulation of session data in userspace 
  117.    if ($hash !==  md5($session.$this->encryption_key)) 
  118.    { 
  119.     log_message('error''The session cookie data did not match what was expected. This could be a possible hacking attempt.'); 
  120.     $this->sess_destroy(); 
  121.     return FALSE; 
  122.    } 
  123.   } 
  124.   // 反序列化存入cookie的session数组 
  125.   $session = $this->_unserialize($session); 
  126.   // 检测session的数据 
  127.   if ( ! is_array($session) OR ! isset($session['session_id']) OR ! isset($session['ip_address']) OR ! isset($session['user_agent']) OR ! isset($session['last_activity'])) 
  128.   { 
  129.    $this->sess_destroy(); 
  130.    return FALSE; 
  131.   } 
  132.   // session数据过期了吗 
  133.   if (($session['last_activity'] + $this->sess_expiration) < $this->now) 
  134.   { 
  135.    $this->sess_destroy(); 
  136.    return FALSE; 
  137.   } 
  138.   // 是否是根据用户ip来读取session数据 
  139.   if ($this->sess_match_ip == TRUE AND $session['ip_address'] != $this->CI->input->ip_address()) 
  140.   { 
  141.    $this->sess_destroy(); 
  142.    return FALSE; 
  143.   } 
  144.   // 是否根据ua来匹配session数据 
  145.   if ($this->sess_match_useragent == TRUE AND trim($session['user_agent']) != trim(substr($this->CI->input->user_agent(), 0, 120))) 
  146.   { 
  147.    $this->sess_destroy(); 
  148.    return FALSE; 
  149.   } 
  150.   // 如果session入库了,这种方式应该更安全点,当然负载大时,就不建议了,数据库读写压力大 
  151.   if ($this->sess_use_database === TRUE) 
  152.   { 
  153.    $this->CI->db->where('session_id'$session['session_id']); 
  154.    if ($this->sess_match_ip == TRUE) 
  155.    { 
  156.     $this->CI->db->where('ip_address'$session['ip_address']); 
  157.    } 
  158.    if ($this->sess_match_useragent == TRUE) 
  159.    { 
  160.     $this->CI->db->where('user_agent'$session['user_agent']); 
  161.    } 
  162.    $query = $this->CI->db->get($this->sess_table_name); 
  163.    // No result?  Kill it! 
  164.    if ($query->num_rows() == 0) 
  165.    { 
  166.     $this->sess_destroy(); 
  167.     return FALSE; 
  168.    } 
  169.    // Is there custom data?  If so, add it to the main session array 
  170.    $row = $query->row(); 
  171.    if (isset($row->user_data) AND $row->user_data != ''
  172.    { 
  173.     $custom_data = $this->_unserialize($row->user_data); 
  174.     if (is_array($custom_data)) 
  175.     { 
  176.      foreach ($custom_data as $key => $val
  177.      { 
  178.       $session[$key] = $val
  179.      } 
  180.     } 
  181.    } 
  182.   } 
  183.   // Session is valid! 
  184.   $this->userdata = $session
  185.   unset($session); 
  186.   return TRUE; 
  187.  } 
  188.  // -------------------------------------------------------------------- 
  189.  /** 
  190.   * 将读取的session数据写入 
  191.   */ 
  192.  function sess_write() 
  193.  { 
  194.   // 是否写入db 
  195.   if ($this->sess_use_database === FALSE) 
  196.   { 
  197.    $this->_set_cookie(); 
  198.    return
  199.   } 
  200.   // set the custom userdata, the session data we will set in a second 
  201.   $custom_userdata = $this->userdata; 
  202.   $cookie_userdata = array(); 
  203.   // Before continuing, we need to determine if there is any custom data to deal with. 
  204.   // Let's determine this by removing the default indexes to see if there's anything left in the array 
  205.   // and set the session data while we're at it 
  206.   foreach (array('session_id','ip_address','user_agent','last_activity'as $val
  207.   { 
  208.    unset($custom_userdata[$val]); 
  209.    $cookie_userdata[$val] = $this->userdata[$val]; 
  210.   } 
  211.   // Did we find any custom data?  If not, we turn the empty array into a string 
  212.   // since there's no reason to serialize and store an empty array in the DB 
  213.   if (count($custom_userdata) === 0) 
  214.   { 
  215.    $custom_userdata = ''
  216.   } 
  217.   else 
  218.   { 
  219.    // Serialize the custom data array so we can store it 
  220.    $custom_userdata = $this->_serialize($custom_userdata); 
  221.   } 
  222.   // 更新session记录 
  223.   $this->CI->db->where('session_id'$this->userdata['session_id']); 
  224.   $this->CI->db->update($this->sess_table_name, array('last_activity' => $this->userdata['last_activity'], 'user_data' => $custom_userdata)); 
  225.   // Write the cookie.  Notice that we manually pass the cookie data array to the 
  226.   // _set_cookie() function. Normally that function will store $this->userdata, but 
  227.   // in this case that array contains custom data, which we do not want in the cookie. 
  228.   $this->_set_cookie($cookie_userdata); 
  229.  } 
  230.  // -------------------------------------------------------------------- 
  231.  /** 
  232.   * 创建一个新的session 
  233.   */ 
  234.  function sess_create() 
  235.  { 
  236.                 //保证sessid 安全唯一 
  237.   $sessid = ''
  238.   while (strlen($sessid) < 32) 
  239.   { 
  240.    $sessid .= mt_rand(0, mt_getrandmax()); 
  241.   } 
  242.   $sessid .= $this->CI->input->ip_address(); 
  243.   $this->userdata = array
  244.        'session_id' => md5(uniqid($sessid, TRUE)), 
  245.        'ip_address' => $this->CI->input->ip_address(), 
  246.        'user_agent' => substr($this->CI->input->user_agent(), 0, 120), 
  247.        'last_activity' => $this->now, 
  248.        'user_data'  => '' 
  249.        ); 
  250.   // Save the data to the DB if needed 
  251.   if ($this->sess_use_database === TRUE) 
  252.   { 
  253.    $this->CI->db->query($this->CI->db->insert_string($this->sess_table_name, $this->userdata)); 
  254.   } 
  255.   // Write the cookie 
  256.   $this->_set_cookie(); 
  257.  } 
  258.  // -------------------------------------------------------------------- 
  259.  /** 
  260.   * 更新session 
  261.   */ 
  262.  function sess_update() 
  263.  { 
  264.   // 默认五分钟更新 
  265.   if (($this->userdata['last_activity'] + $this->sess_time_to_update) >= $this->now) 
  266.   { 
  267.    return
  268.   } 
  269.   // Save the old session id so we know which record to 
  270.   // update in the database if we need it 
  271.   $old_sessid = $this->userdata['session_id']; 
  272.   $new_sessid = ''
  273.   while (strlen($new_sessid) < 32) 
  274.   { 
  275.    $new_sessid .= mt_rand(0, mt_getrandmax()); 
  276.   } 
  277.   // To make the session ID even more secure we'll combine it with the user's IP 
  278.   $new_sessid .= $this->CI->input->ip_address(); 
  279.   // Turn it into a hash 
  280.   $new_sessid = md5(uniqid($new_sessid, TRUE)); 
  281.   // Update the session data in the session data array 
  282.   $this->userdata['session_id'] = $new_sessid
  283.   $this->userdata['last_activity'] = $this->now; 
  284.   // _set_cookie() will handle this for us if we aren't using database sessions 
  285.   // by pushing all userdata to the cookie. 
  286.   $cookie_data = NULL; 
  287.   // 更新数据库中几率 
  288.   if ($this->sess_use_database === TRUE) 
  289.   { 
  290.    // set cookie explicitly to only have our session data 
  291.    $cookie_data = array(); 
  292.    foreach (array('session_id','ip_address','user_agent','last_activity'as $val
  293.    { 
  294.     $cookie_data[$val] = $this->userdata[$val]; 
  295.    } 
  296.    $this->CI->db->query($this->CI->db->update_string($this->sess_table_name, array('last_activity' => $this->now, 'session_id' => $new_sessid), array('session_id' => $old_sessid))); 
  297.   } 
  298.   // 重新写入session 
  299.   $this->_set_cookie($cookie_data); 
  300.  } 
  301.  // -------------------------------------------------------------------- 
  302.  /** 
  303.   *销毁当前所有session数据 
  304.   */ 
  305.  function sess_destroy() 
  306.  { 
  307.   // Kill the session DB row 
  308.   if ($this->sess_use_database === TRUE && isset($this->userdata['session_id'])) 
  309.   { 
  310.    $this->CI->db->where('session_id'$this->userdata['session_id']); 
  311.    $this->CI->db->delete($this->sess_table_name); 
  312.   } 
  313.   // Kill the cookie 
  314.   setcookie( 
  315.      $this->sess_cookie_name, 
  316.      addslashes(serialize(array())), 
  317.      ($this->now - 31500000), 
  318.      $this->cookie_path, 
  319.      $this->cookie_domain, 
  320.      0 
  321.     ); 
  322.   // Kill session data 
  323.   $this->userdata = array(); 
  324.  } 
  325.  // -------------------------------------------------------------------- 
  326.  /** 
  327.   * 获取session数组指定元素的值 
  328.   */ 
  329.  function userdata($item
  330.  { 
  331.   return ( ! isset($this->userdata[$item])) ? FALSE : $this->userdata[$item]; 
  332.  } 
  333.  // -------------------------------------------------------------------- 
  334.  /** 
  335.   * 获取所有session数据 
  336.   */ 
  337.  function all_userdata() 
  338.  { 
  339.   return $this->userdata; 
  340.  } 
  341.  // -------------------------------------------------------------------- 
  342.  /** 
  343.   * 添加和修改自定义的session数据 
  344.   */ 
  345.  function set_userdata($newdata = array(), $newval = ''
  346.  { 
  347.   if (is_string($newdata)) 
  348.   { 
  349.    $newdata = array($newdata => $newval); 
  350.   } 
  351.                 //支持数组组合方式 
  352.   if (count($newdata) > 0) 
  353.   { 
  354.    foreach ($newdata as $key => $val
  355.    { 
  356.     $this->userdata[$key] = $val
  357.    } 
  358.   } 
  359.   $this->sess_write(); 
  360.  } 
  361.  // -------------------------------------------------------------------- 
  362.  /** 
  363.   * 删除session数组中的元素, 
  364.   */ 
  365.  function unset_userdata($newdata = array()) 
  366.  { 
  367.   if (is_string($newdata)) 
  368.   { 
  369.    $newdata = array($newdata => ''); 
  370.   } 
  371.   if (count($newdata) > 0) 
  372.   { 
  373.    foreach ($newdata as $key => $val
  374.    { 
  375.     unset($this->userdata[$key]); 
  376.    } 
  377.   } 
  378.   $this->sess_write(); 
  379.  } 
  380.  // ------------------------------------------------------------------------ 
  381.  /** 
  382.   * Add or change flashdata, only available 
  383.   * until the next request 
  384.   * 
  385.   * @access public 
  386.   * @param mixed 
  387.   * @param string 
  388.   * @return void 
  389.   */ 
  390.  function set_flashdata($newdata = array(), $newval = ''
  391.  { 
  392.   if (is_string($newdata)) 
  393.   { 
  394.    $newdata = array($newdata => $newval); 
  395.   } 
  396.   if (count($newdata) > 0) 
  397.   { 
  398.    foreach ($newdata as $key => $val
  399.    { 
  400.     $flashdata_key = $this->flashdata_key.':new:'.$key
  401.     $this->set_userdata($flashdata_key$val); 
  402.    } 
  403.   } 
  404.  } 
  405.  // ------------------------------------------------------------------------ 
  406.  /** 
  407.   * CI支持闪出数据也就是说 Session数据只对下次服务器请求可用,有时候如果你还想在下个请求后的请求还有效。。。 
  408.          * keep_flashdata功能就是讲持续保持闪出数据,使其在下个请求也有效 
  409.   */ 
  410.  function keep_flashdata($key
  411.  { 
  412.                 //将闪出数据标记成new 
  413.   $old_flashdata_key = $this->flashdata_key.':old:'.$key
  414.   $value = $this->userdata($old_flashdata_key); 
  415.   $new_flashdata_key = $this->flashdata_key.':new:'.$key
  416.   $this->set_userdata($new_flashdata_key$value); 
  417.  } 
  418.  // ------------------------------------------------------------------------ 
  419.  /** 
  420.   * 获取闪出数据 
  421.   */ 
  422.  function flashdata($key
  423.  { 
  424.   $flashdata_key = $this->flashdata_key.':old:'.$key
  425.   return $this->userdata($flashdata_key); 
  426.  } 
  427.  // ------------------------------------------------------------------------ 
  428.  /** 
  429.   * 将闪出数据标记成old,以便_flashdata_sweep清除数据 
  430.   */ 
  431.  function _flashdata_mark() 
  432.  { 
  433.   $userdata = $this->all_userdata(); 
  434.   foreach ($userdata as $name => $value
  435.   { 
  436.    $parts = explode(':new:'$name); 
  437.    if (is_array($parts) && count($parts) === 2) 
  438.    { 
  439.     $new_name = $this->flashdata_key.':old:'.$parts[1]; 
  440.     $this->set_userdata($new_name$value); 
  441.     $this->unset_userdata($name); 
  442.    } 
  443.   } 
  444.  } 
  445.  // ------------------------------------------------------------------------ 
  446.  /** 
  447.   * 将标记成old的闪出数据闪出 
  448.   */ 
  449.  function _flashdata_sweep() 
  450.  { 
  451.   $userdata = $this->all_userdata(); 
  452.   foreach ($userdata as $key => $value
  453.   { 
  454.    if (strpos($key':old:')) 
  455.    { 
  456.     $this->unset_userdata($key); 
  457.    } 
  458.   } 
  459.  } 
  460.  //获取当前时间 
  461.  function _get_time() 
  462.  { 
  463.   if (strtolower($this->time_reference) == 'gmt'
  464.   { 
  465.    $now = time(); 
  466.    $time = mktime(gmdate("H"$now), gmdate("i"$now), gmdate("s"$now), gmdate("m"$now), gmdate("d"$now), gmdate("Y"$now)); 
  467.   } 
  468.   else 
  469.   { 
  470.    $time = time(); 
  471.   } 
  472.   return $time
  473.  } 
  474.  // -------------------------------------------------------------------- 
  475.  /** 
  476.   * 写入session cookie 
  477.   * 
  478.   */ 
  479.  function _set_cookie($cookie_data = NULL) 
  480.  { 
  481.   if (is_null($cookie_data)) 
  482.   { 
  483.    $cookie_data = $this->userdata; 
  484.   } 
  485.   // 序列化数组 
  486.   $cookie_data = $this->_serialize($cookie_data); 
  487.                 //加密数据 
  488.   if ($this->sess_encrypt_cookie == TRUE) 
  489.   { 
  490.    $cookie_data = $this->CI->encrypt->encode($cookie_data); 
  491.   } 
  492.   else 
  493.   { 
  494.    // if encryption is not used, we provide an md5 hash to prevent userside tampering 
  495.    $cookie_data = $cookie_data.md5($cookie_data.$this->encryption_key); 
  496.   } 
  497.                 //sess_expire_on_close为TRUE则,浏览器关闭,session失效 
  498.   $expire = ($this->sess_expire_on_close === TRUE) ? 0 : $this->sess_expiration + time(); 
  499.   // Set the cookie 
  500.   setcookie( 
  501.      $this->sess_cookie_name, 
  502.      $cookie_data
  503.      $expire
  504.      $this->cookie_path, 
  505.      $this->cookie_domain, 
  506.      $this->cookie_secure 
  507.     ); 
  508.  } 
  509.  // -------------------------------------------------------------------- 
  510.  /** 
  511.   * 序列化数组 
  512.   */ 
  513.  function _serialize($data
  514.  { 
  515.   if (is_array($data)) 
  516.   { 
  517.    foreach ($data as $key => $val
  518.    { 
  519.     if (is_string($val)) 
  520.     { 
  521.      $data[$key] = str_replace('\\', '{{slash}}', $val); 
  522.     } 
  523.    } 
  524.   } 
  525.   else 
  526.   { 
  527.    if (is_string($data)) 
  528.    { 
  529.     $data = str_replace('\\', '{{slash}}', $data); 
  530.    } 
  531.   } 
  532.   return serialize($data); 
  533.  } 
  534.  // -------------------------------------------------------------------- 
  535.  /** 
  536.   * 反序列化数组 
  537.   */ 
  538.  function _unserialize($data
  539.  { 
  540.   $data = @unserialize(strip_slashes($data)); 
  541.   if (is_array($data)) 
  542.   { 
  543.    foreach ($data as $key => $val
  544.    { 
  545.     if (is_string($val)) 
  546.     { 
  547.      $data[$key] = str_replace('{{slash}}''\\'$val); 
  548.     } 
  549.    } 
  550.    return $data
  551.   } 
  552.   return (is_string($data)) ? str_replace('{{slash}}''\\'$data) : $data
  553.  } 
  554.  // -------------------------------------------------------------------- 
  555.  /** 
  556.   * 回收/删除数据库中失效的session信息 
  557.   */ 
  558.  function _sess_gc() 
  559.  { 
  560.   if ($this->sess_use_database != TRUE) 
  561.   { 
  562.    return
  563.   } 
  564.   srand(time()); 
  565.   if ((rand() % 100) < $this->gc_probability) 
  566.   { 
  567.    $expire = $this->now - $this->sess_expiration; 
  568.    $this->CI->db->where("last_activity < {$expire}"); 
  569.    $this->CI->db->delete($this->sess_table_name); 
  570.    log_message('debug''Session garbage collection performed.'); 
  571.   } 
  572.  } 

Tags: CI框架 Session php

分享到: