设为首页
收藏本站
最新电影
您现在的位置: 首页=>后台技术=>php 订阅本栏目  
ECSHOP的SESSION机制
时间: 2010-06-27 14:32:11 阅读次数:4172


最近在做ECSHOP和UCENTERHOME的二次开发,有幸接触到了ECSHOP的SESSION机制,个人认为这种机制相比PHP自带的SESSION机制拥有更高的安全性和可移植性,尤其是在做多系统共享SESSION时优势更为明显,因此简单的做了一下的代码分析,如有错误,请留言指点

<?php
/**
* 仅以ECSHOP的cls_session.php做的代码分析,如需使用到其他项目中,请自行重新封装
*
* @copyright Copyright (c) 2009-2010 (<a href="http://www.wang-lei.com.cn飞飞As.p技术乐园)" target="_blank">http://www.wang-lei.com.cn)</a>
* @author e0v1a(夜露使徒) <e0v1a@163.com飞飞Asp技!术乐园>
*/
class cls_session
{
var $db = NULL;
var $session_table = '';
var $max_life_time = 1800; // SESSION 过期时间
var $session_name = '';
var $session_id = '';
var $session_expiry = '';
var $session_md5 = '';
var $session_cookie_path = '/';
var $session_cookie_domain = '';
var $session_cookie_secure = false;
var $_ip = '';
var $_time = 0;
//PHP5的构造函数,用于创建一个SESSION对象,为了保证对PHP4的兼容,其实是在该函数内部调用了和类同名的函数
function __construct(&$db, $session_table, $session_data_table, $session_name = 'ECS_ID', $session_id = '')
{
$this->cls_session($db, $session_table, $session_data_table, $session_name, $session_id);
}
/**
* 这里才是真正的构造函数,也是在数据库中模拟SESSION的重点
*
* @param object $db 数据库对象
* @param string $session_table SESSION表名
* @param string $session_data_table 扩展SESSION表名
* @param string $session_name COOKIE中的SESSION名
* @param string $session_id SESSIONID,起到唯一识别的作用
* @return cls_session
*/
function cls_session(&$db, $session_table, $session_data_table, $session_name = 'ECS_ID', $session_id = '')
{
//将系统自带的$_SESSION初始化为一个空数组并放进全局函数里
$GLOBALS['_SESSION'] = array();
//根据配置文件设置相关的类属性
if (!empty($GLOBALS['cookie_path']))
{
$this->session_cookie_path = $GLOBALS['cookie_path'];
}
else
{
$this->session_cookie_path = '/';
}
if (!empty($GLOBALS['cookie_domain']))
{
$this->session_cookie_domain = $GLOBALS['cookie_domain'];
}
else
{
$this->session_cookie_domain = '';
}
//这里稍微提一下,如果你使用的是HTTPS连接,那么这里需要设置为true
if (!empty($GLOBALS['cookie_secure']))
{
$this->session_cookie_secure = $GLOBALS['cookie_secure'];
}
else
{
$this->session_cookie_secure = false;
}
$this->session_name = $session_name;
$this->session_table = $session_table;
$this->session_data_table = $session_data_table;
$this->db = &$db;
$this->_ip = real_ip();
//如果先前COOKIE中已经保存了sessionid则将他的值赋给session_id属性
if ($session_id == '' && !empty($_COOKIE[$this->session_name]))
{
$this->session_id = $_COOKIE[$this->session_name];
}
else
{
$this->session_id = $session_id;
}
if ($this->session_id)
{
//如果COOKIE中已经存在session_id,取他的前32位做CRC32位校检保证值的正确性
$tmp_session_id = substr($this->session_id, 0, 32);
if ($this->gen_session_key($tmp_session_id) == substr($this->session_id, 32))
{
$this->session_id = $tmp_session_id;
}
else
{
$this->session_id = '';
}
}
$this->_time = time();
if ($this->session_id)
{
//从数据库中取对应session_id的记录保存到$_SESSION中
$this->load_session();
}
else
{
//如果COOKIE中无值或者没能通过校检则创建一个全新且唯一的session_id
$this->gen_session_id();
//写COOKIE
setcookie($this->session_name, $this->session_id . $this->gen_session_key($this->session_id), 0, $this->session_cookie_path, $this->session_cookie_domain, $this->session_cookie_secure);
}
/*这个类中最有用的地方,大家可以查一下手册,register_shutdown_function这个函数有些类似于PHP5中的析构函数,
不同的是他是在页面执行完后回调参数内的函数,在这个类中他调用的是类中的close_session()这个方法,从而实现了在
每个页面执行完后更新$_SESSION并且随机性的触发删除超过过期时间的SESSION
*/
register_shutdown_function(array(&$this, 'close_session'));
}
//生成一个唯一的session_id
function gen_session_id()
{
$this->session_id = md5(uniqid(mt_rand(), true));
return $this->insert_session();
}
//对COOKIE中的session_id做CRC32校检
function gen_session_key($session_id)
{
static $ip = '';
if ($ip == '')
{
$ip = substr($this->_ip, 0, strrpos($this->_ip, '.'));
}
return sprintf('%08x', crc32(!empty($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] . ROOT_PATH . $ip . $session_id : ROOT_PATH . $ip . $session_id));
}
//如果数据库中对应COOKIE内session_id的记录已经不存在,则创建一条对应的空记录
function insert_session()
{
return $this->db->query('INSERT INTO ' . $this->session_table . " (sesskey, expiry, ip, data) VALUES ('" . $this->session_id . "', '". $this->_time ."', '". $this->_ip ."', 'a:0:{}')");
}
function load_session()
{
//检索对应session_id的记录,如果不存在则创建一条空记录
$session = $this->db->getRow('SELECT userid, adminid, user_name, user_rank, discount, email, data, expiry FROM ' . $this->session_table . " WHERE sesskey = '" . $this->session_id . "'");
if (empty($session))
{
$this->insert_session();
$this->session_expiry = 0;
$this->session_md5 = '40cd750bba9870f18aada2478b24840a';
$GLOBALS['_SESSION'] = array();
}
else
{
//如果在SESSION表中检索到对应数据,且SESSION还没有过期则进行相关赋值操作
if (!empty($session['data']) && $this->_time - $session['expiry'] <= $this->max_life_time)
{
$this->session_expiry = $session['expiry'];
//对记录的值做一个MD5校检,以检测后边SESSION值是否发生变化
$this->session_md5 = md5($session['data']);
//反序列化$session['data']将值存到$_SESSION中
$GLOBALS['_SESSION'] = unserialize($session['data']);
$GLOBALS['_SESSION']['user_id'] = $session['userid'];
$GLOBALS['_SESSION']['admin_id'] = $session['adminid'];
$GLOBALS['_SESSION']['user_name'] = $session['user_name'];
$GLOBALS['_SESSION']['user_rank'] = $session['user_rank'];
$GLOBALS['_SESSION']['discount'] = $session['discount'];
$GLOBALS['_SESSION']['email'] = $session['email'];
}
else
{
//否则说明SESSION值超过255字节,则到SESSION_DATA_TABLE表里边查询值
$session_data = $this->db->getRow('SELECT data, expiry FROM ' . $this->session_data_table . " WHERE sesskey = '" . $this->session_id . "'");
//如果在SESSION_DATA_TABLE表中检索到对应数据,且SESSION还没有过期则进行相关赋值操作
if (!empty($session_data['data']) && $this->_time - $session_data['expiry'] <= $this->max_life_time)
{
$this->session_expiry = $session_data['expiry'];
$this->session_md5 = md5($session_data['data']);
$GLOBALS['_SESSION'] = unserialize($session_data['data']);
$GLOBALS['_SESSION']['user_id'] = $session['userid'];
$GLOBALS['_SESSION']['admin_id'] = $session['adminid'];
$GLOBALS['_SESSION']['user_name'] = $session['user_name'];
$GLOBALS['_SESSION']['user_rank'] = $session['user_rank'];
$GLOBALS['_SESSION']['discount'] = $session['discount'];
$GLOBALS['_SESSION']['email'] = $session['email'];
}
else
{
$this->session_expiry = 0;
$this->session_md5 = '40cd750bba9870f18aada2478b24840a';
$GLOBALS['_SESSION'] = array();
}
}
}
}
function update_session()
{
//取得页面中$_SESSION变量内的值
$adminid = !empty($GLOBALS['_SESSION']['admin_id']) ? intval($GLOBALS['_SESSION']['admin_id']) : 0;
$userid = !empty($GLOBALS['_SESSION']['user_id']) ? intval($GLOBALS['_SESSION']['user_id']) : 0;
$user_name = !empty($GLOBALS['_SESSION']['user_name']) ? trim($GLOBALS['_SESSION']['user_name']) : 0;
$user_rank = !empty($GLOBALS['_SESSION']['user_rank']) ? intval($GLOBALS['_SESSION']['user_rank']) : 0;
$discount = !empty($GLOBALS['_SESSION']['discount']) ? round($GLOBALS['_SESSION']['discount'], 2) : 0;
$email = !empty($GLOBALS['_SESSION']['email']) ? trim($GLOBALS['_SESSION']['email']) : 0;
unset($GLOBALS['_SESSION']['admin_id']);
unset($GLOBALS['_SESSION']['user_id']);
unset($GLOBALS['_SESSION']['user_name']);
unset($GLOBALS['_SESSION']['user_rank']);
unset($GLOBALS['_SESSION']['discount']);
unset($GLOBALS['_SESSION']['email']);
$data = serialize($GLOBALS['_SESSION']);
$this->_time = time();
//如果页面上SESSION的值没有发生变化并且最后一次SESSION更新时间距离当前时间小于10秒则跳出函数
if ($this->session_md5 == md5($data) && $this->_time < $this->session_expiry + 10)
{
return true;
}
//转义SESSION的值用于安全入库
$data = addslashes($data);
/*为了保证速度,默认的sessions表data字段是char类型的,最大支持255个字符,当值长度超过255个字符时
将把DATA的值存到SESSION_DATA_TABLE表中
*/
if (isset($data{255}))
{
$this->db->autoReplace($this->session_data_table, array('sesskey' => $this->session_id, 'expiry' => $this->_time, 'data' => $data), array('data' => $data));
$data = '';
}
//更新SESSION个字段的值,主要作用是更新expiry这个字段,以此来判断用户是否处于活动状态
return $this->db->query('UPDATE ' . $this->session_table . " SET expiry = '" . $this->_time . "', ip = '" . $this->_ip . "', userid = '" . $userid . "', adminid = '" . $adminid . "', user_name='" . $user_name . "', user_rank='" . $user_rank . "', discount='" . $discount . "', email='" . $email . "', data = '$data' WHERE sesskey = '" . $this->session_id . "' LIMIT 1");
}
//register_shutdown_function将调用这个函数进行SESSION的更新和删除过期数据的操作
function close_session()
{
$this->update_session();
/* 随机对 sessions_data 的库进行删除操作 */
if (mt_rand(0, 2) == 2)
{
$this->db->query('DELETE FROM ' . $this->session_data_table . ' WHERE expiry < ' . ($this->_time - $this->max_life_time));
}
if ((time() % 2) == 0)
{
return $this->db->query('DELETE FROM ' . $this->session_table . ' WHERE expiry < ' . ($this->_time - $this->max_life_time));
}
return true;
}
function delete_spec_admin_session($adminid)
{
if (!empty($GLOBALS['_SESSION']['admin_id']) && $adminid)
{
return $this->db->query('DELETE FROM ' . $this->session_table . " WHERE adminid = '$adminid'");
}
else
{
return false;
}
}
//清空$_SESSION
function destroy_session()
{
$GLOBALS['_SESSION'] = array();
setcookie($this->session_name, $this->session_id, 1, $this->session_cookie_path, $this->session_cookie_domain, $this->session_cookie_secure);
/* ECSHOP 自定义执行部分 */
if (!empty($GLOBALS['ecs']))
{
$this->db->query('DELETE FROM ' . $GLOBALS['ecs']->table('cart') . " WHERE session_id = '$this->session_id'");
}
/* ECSHOP 自定义执行部分 */
$this->db->query('DELETE FROM ' . $this->session_data_table . " WHERE sesskey = '" . $this->session_id . "' LIMIT 1");
return $this->db->query('DELETE FROM ' . $this->session_table . " WHERE sesskey = '" . $this->session_id . "' LIMIT 1");
}
function get_session_id()
{
return $this->session_id;
}
//得以于这种优秀的机制,现在统计在线活动用户仅需要这么一个简单的查询
function get_users_count()
{
return $this->db->getOne('SELECT count(*) FROM ' . $this->session_table);
}
}
?>





站内搜索    

下一篇很全面的PHP数组的总结

上一篇PHP中单引号和双引号的区别

本栏目最新 栏目最新列表
50个新鲜兼容最新版本的免费WordPress主题
zend studio 添加spket ide的方法,绝对能用
zend studio 字体配色方案修改的方法
css浮动的详细讲解
Windows(iis)下的Memcache安装方法(安装文件
网站优化策划 栏目最新列表
增加网站外链的快速方法
网站上线前必做的30个检查
新的友情链接参考标准(没有google的PR情况
优化Google的AdSense广告的五个工具
王通讲SEO八大基础
站点最新 站点最新列表
微博推广的一些技巧
xhEditor v1.1.7 发布,
收集的一些轻量级非常实
50个新鲜兼容最新版本的
javascript中cookie的设
Excel中出现#VALUE!、#D
jquery插件:飞飞表情插件
十个使用HTML5开发的精彩
支持HTML5的浏览器有哪些
飞妮莫属:漫画:如何写出
历史最热10条信息  
MIME介绍 及 [1] [2] [3]
巧用Google和迅雷来下载
Transact SQL 常 [1] [2]
VIA Rhine II Fast Ethe
电脑常用端 [1] [2] [3]
Do you get a kick out
十道羊皮卷 欣赏+mp3版+
每日一句:A friend and
每日一句:Theres no tu
经典__悟透JavaScript
相关专题 相关专题列表
很全面的PHP数组的总结
file_put_contents用法解
PHP magic_quotes_gpc的
PHP中__construct(), __
php4,php5构造函数的定义
str_replace函数用法、定
ini_get、ini_set、ini_
set_time_limit函数定义
error_reporting()用法及
get_magic_quotes_gpc
 width= 
伟哥博客 西安房产 123最新电影 三四六四