Skip to content

Instantly share code, notes, and snippets.

@zhlifly
Forked from zhouming/MY_Session.php
Last active February 17, 2020 12:48
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save zhlifly/7746854 to your computer and use it in GitHub Desktop.
Save zhlifly/7746854 to your computer and use it in GitHub Desktop.
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class MY_Session extends CI_Session{
private $sess_use_redis = TRUE;
private $redis = '';
public function __construct($params = array()) {
//parent::__construct();
$this->CI =& get_instance();
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)
{
$this->$key = (isset($params[$key])) ? $params[$key] : $this->CI->config->item($key);
}
if ($this->encryption_key == '')
{
show_error('In order to use the Session class you are required to set an encryption key in your config file.');
}
// Load the string helper so we can use the strip_slashes() function
$this->CI->load->helper('string');
// Do we need encryption? If so, load the encryption class
if ($this->sess_encrypt_cookie == TRUE)
{
$this->CI->load->library('encrypt');
}
if ($this->sess_use_redis == TRUE) {
$this->redis = new Redis();
$this->redis->connect($this->CI->config->item('redis_host'), $this->CI->config->item('redis_port'));
//$this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP); //php5.4 can not work
$this->redis->setOption(Redis::OPT_PREFIX, 'sessions:');
}
// Set the "now" time. Can either be GMT or server time, based on the
// config prefs. We use this to set the "last activity" time
$this->now = $this->_get_time();
// Set the session length. If the session expiration is
// set to zero we'll set the expiration two years from now.
if ($this->sess_expiration == 0)
{
$this->sess_expiration = (60*60*24*365*2);
}
// Set the cookie name
$this->sess_cookie_name = $this->cookie_prefix.$this->sess_cookie_name;
// Run the Session routine. If a session doesn't exist we'll
// create a new one. If it does, we'll update it.
if ( ! $this->sess_read()) {
$this->sess_create();
} else {
$this->sess_update();
}
// Delete 'old' flashdata (from last request)
$this->_flashdata_sweep();
// Mark all new flashdata as old (data will be deleted before next request)
$this->_flashdata_mark();
// Delete expired sessions if necessary
$this->_sess_gc();
log_message('debug', "Session routines successfully run");
}
function sess_read() {
$session = array();
// Fetch the cookie
$session_id = $this->CI->input->cookie($this->sess_cookie_name);
// No cookie? Goodbye cruel world!...
if ($session_id === FALSE) {
log_message('debug', 'A session cookie was not found.');
return FALSE;
}
$session['session_id'] = $session_id;
$session['ip_address'] = $this->CI->input->ip_address();
$session['user_agent'] = trim(substr($this->CI->input->user_agent(), 0, 120));
// Is there a corresponding session in the DB?
if ($this->sess_use_redis=== TRUE) {
//$this->CI->db->where('session_id', $session['session_id']);
$row = json_decode($this->redis->get($session['session_id']), TRUE);
if ( ! $row) {
$this->sess_destroy();
return FALSE;
}
if ($this->sess_match_ip == TRUE AND $row['ip_address'] != $session['ip_address']) {
$this->sess_destroy();
return FALSE;
}
if ($this->sess_match_useragent == TRUE AND trim($row['user_agent']) != $session['user_agent']) {
$this->sess_destroy();
return FALSE;
}
// Is there custom data? If so, add it to the main session array
$session['last_activity'] = $row['last_activity'];
if (isset($row['user_data']) AND $row['user_data'] != '') {
$custom_data = $this->_unserialize($row['user_data']);
if (is_array($custom_data)) {
foreach ($custom_data as $key => $val) {
$session[$key] = $val;
}
}
}
}
// Session is valid!
$this->userdata = $session;
unset($session);
return TRUE;
}
function sess_create() {
$sessid = '';
while (strlen($sessid) < 32) {
$sessid .= mt_rand(0, mt_getrandmax());
}
// To make the session ID even more secure we'll combine it with the user's IP
$sessid .= $this->CI->input->ip_address();
$this->userdata = array(
'session_id' => md5(uniqid($sessid, TRUE)),
'ip_address' => $this->CI->input->ip_address(),
'user_agent' => substr($this->CI->input->user_agent(), 0, 120),
'last_activity' => $this->now,
'user_data' => ''
);
// Save the data to the DB if needed
if ($this->sess_use_redis === TRUE) {
$has_session = $this->redis->get($this->userdata['session_id']);
if ($has_session) {
$this->sess_create();
return FALSE;
}
$this->redis->setex($this->userdata['session_id'], $this->sess_expiration, json_encode($this->userdata));
}
// Write the cookie
$this->_set_cookie();
}
function sess_update() {
// We only update the session every five minutes by default
if (($this->userdata['last_activity'] + $this->sess_time_to_update) >= $this->now) {
return;
}
// Save the old session id so we know which record to
// update in the database if we need it
$old_sessid = $this->userdata['session_id'];
$new_sessid = '';
while (strlen($new_sessid) < 32) {
$new_sessid .= mt_rand(0, mt_getrandmax());
}
// To make the session ID even more secure we'll combine it with the user's IP
$new_sessid .= $this->CI->input->ip_address();
// Turn it into a hash
$new_sessid = md5(uniqid($new_sessid, TRUE));
// Update the session data in the session data array
$this->userdata['session_id'] = $new_sessid;
$this->userdata['last_activity'] = $this->now;
// _set_cookie() will handle this for us if we aren't using database sessions
// by pushing all userdata to the cookie.
// Update the session ID and last_activity field in the DB if needed
// set cookie explicitly to only have our session data
$cookie_data = array();
$custom_userdata = $this->userdata;
foreach (array('session_id','ip_address','user_agent','last_activity') as $val) {
unset($custom_userdata[$val]);
$cookie_data[$val] = $this->userdata[$val];
}
if (count($custom_userdata) === 0) {
$custom_userdata = '';
} else {
if (isset($custom_userdata['user_data'])) {
unset($custom_userdata['user_data']);
}
$custom_userdata = $this->_serialize($custom_userdata);
}
if ($this->sess_use_redis === TRUE) {
$has_session = $this->redis->get($new_sessid);
if ($has_session) {
$this->sess_update();
return FALSE;
}
$this->userdata['user_data'] = $custom_userdata;
$this->redis->setex($new_sessid, $this->sess_expiration, json_encode($this->userdata));
$this->redis->delete($old_sessid);
}
// Write the cookie
$this->_set_cookie();
}
function sess_write() {
// Are we saving custom data to the DB? If not, all we do is update the cookie
if ($this->sess_use_redis === FALSE) {
$this->_set_cookie();
return;
}
// set the custom userdata, the session data we will set in a second
$custom_userdata = $this->userdata;
$cookie_userdata = array();
// Before continuing, we need to determine if there is any custom data to deal with.
// Let's determine this by removing the default indexes to see if there's anything left in the array
// and set the session data while we're at it
foreach (array('session_id','ip_address','user_agent','last_activity') as $val) {
unset($custom_userdata[$val]);
$cookie_userdata[$val] = $this->userdata[$val];
}
// Did we find any custom data? If not, we turn the empty array into a string
// since there's no reason to serialize and store an empty array in the DB
if (count($custom_userdata) === 0) {
$custom_userdata = '';
} else {
// Serialize the custom data array so we can store it
if (isset($custom_userdata['user_data'])) {
unset($custom_userdata['user_data']);
}
$custom_userdata = $this->_serialize($custom_userdata);
}
// Run the update query
$this->userdata['user_data'] = $custom_userdata;
$this->redis->multi()
->delete($this->userdata['session_id'])
->setex($this->userdata['session_id'], $this->sess_expiration, json_encode($this->userdata))
->exec();
// Write the cookie. Notice that we manually pass the cookie data array to the
// _set_cookie() function. Normally that function will store $this->userdata, but
// in this case that array contains custom data, which we do not want in the cookie.
$this->_set_cookie();
}
function _set_cookie($cookie_data = NULL) {
$expire = ($this->sess_expire_on_close === TRUE) ? 0 : $this->sess_expiration + time();
// Set the cookie
setcookie(
$this->sess_cookie_name,
$this->userdata['session_id'],
$expire,
$this->cookie_path,
$this->cookie_domain,
$this->cookie_secure
);
}
function _sess_gc() {
if ($this->sess_use_redis == TRUE) {
return;
}
}
function sess_destroy() {
// Kill the session DB row
if ($this->sess_use_redis === TRUE AND isset($this->userdata['session_id'])) {
$this->redis->delete($this->userdata['session_id']);
}
// Kill the cookie
setcookie(
$this->sess_cookie_name,
addslashes(serialize(array())),
($this->now - 31500000),
$this->cookie_path,
$this->cookie_domain,
0
);
}
}
// END MY_Session Class
/* End of file MY_Session.php */
/* Location: ./libraries/MY_Session.php */
@irla
Copy link

irla commented Feb 26, 2014

Hi, great snippet. I have one question.

How do you make it $this->redis = new Redis(); working? Which library are you using and how do you load it to CI that Redis class is visible?

Thanks In Advance!

@Kufert
Copy link

Kufert commented Mar 10, 2014

follow this post to find out how to install phpredis. This will allow you to use $this->redis = new Redis();

@fz19
Copy link

fz19 commented Sep 21, 2016

this class occur concurrency issue at same time. (if session id changed, sometimes session destroyed)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment