1、如果单靠session自己的垃圾回收机制,时间久了,保存session的文件会越来越多,影响查找效率;
2、对于需要统计同时在线用户的系统,实现起来很不方便;
3、分布式系统难以共享session。
如果将session.save_handler设置为user,php可以通过session_set_save_handler函数来重载session的几个底层会话处理方法,以达到使用数据库来保存session的目的,下面以mysql数据库为例,数据库连接方式采用PDO。
session.sql
CREATE TABLE `session` ( `session_id` varchar(32) NOT NULL default '', `session_content` text NOT NULL, `last_visit` int(11) NOT NULL, PRIMARY KEY (`session_id`) ) ENGINE=MyISAM;此处建议使用ENGINE=MEMORY,MEMORY引擎采用内存表,所有数据存储在内存,操作速度快,对于session这种形式的数据正好适用。
session.php
<?PHP // -------------------------------------------------------------------------- // File name : session.php // Description : 数据库存放session // Copyright(C), MagicLab.cn, 2008, All Rights Reserved. // Author: xinglu QQ:330708730 MSN:xinglu_1983@hotmail.com // -------------------------------------------------------------------------- class Session { var $lifeTime; var $domain; var $dbh; function __construct() { //定义生存期 $this->lifeTime = 3600; //定义域 $this->domain = $_SERVER['SERVER_NAME']; try { $this->dbh = new PDO('mysql:host=localhost;dbname=session', 'root', ''); $this->dbh->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER); //PDO出错方式 跑出异常 $this->dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } catch (PDOException $e) { throw $e; } //设置session生存期 ini_set('session.gc_maxlifetime', $this->lifeTime); //设置客户端使用COOKIE保存SESSIONID ini_set('session.use_cookies', 1); ini_set('session.cookie_path', '/'); //多主机共享保存SESSIONID的COOKIE(使二级域名可以共享session) ini_set('session.cookie_domain', $this->domain); //设置客户端保存 SESSIONID 的cookie的生存期 ini_set('session.cookie_lifetime', $this->lifeTime); //为保持客户端cookie生命期与服务端session生命期相同,进行写cookie操作 $sessionid = session_id(); $sessionname = session_name(); setcookie($sessionname, $sessionid, $this->lifeTime, '/', $this->domain); //将session.save_handler设置为user,而不是默认的 files session_module_name('user'); //重载session函数 session_set_save_handler(array(&$this,"open"), array(&$this,"close"), array(&$this,"read"), array(&$this,"write"), array(&$this,"destroy"), array(&$this,"gc") ); } function open($savePath, $sessName) { $this->gc(); return true; } function close() { $this->gc(); unset($this->dbh); return true; } function read($session_id) { if (!isset($session_id)) return ""; $last = time() - $this->lifeTime; $sql = sprintf("SELECT session_content FROM session WHERE session_id = '%s' AND last_visit > ".$last ,$session_id); try { $query = $this->dbh->query($sql); if($row = $query->fetch(PDO::FETCH_ASSOC)) return $row['session_content']; } catch(PDOException $e) { echo $e->getMessage(); die(); } return ""; } function write($session_id, $session_content) { if (!isset($session_id)) return ""; $sql = sprintf("SELECT * FROM session WHERE session_id='%s'", $session_id); try { $sth = $this->dbh->query($sql); $rs = $sth->fetchAll(PDO::FETCH_ASSOC); if (is_array($rs) && !empty($rs)) { $sql = sprintf("UPDATE session SET session_content='%s',last_visit=%d WHERE session_id='%s';" ,$session_content ,time() ,$session_id ); } else { $sql = sprintf("INSERT INTO session SET session_id='%s',session_content='%s',last_visit=%d;" ,$session_id ,$session_content ,time() ); } $rs = $this->dbh->exec($sql); if($rs) return true; } catch(PDOException $e) { echo $e->getMessage(); die(); } return false; } function destroy($session_id) { if (!isset($session_id)) return true; $sql = sprintf("DELETE FROM session WHERE session_id = '%s'", $session_id); try { $rs = $this->dbh->exec($sql); if($rs) return true; } catch(PDOException $e) { echo $e->getMessage(); die(); } return false; } function gc() { $last = time() - $this->lifeTime; $sql = sprintf("DELETE FROM session WHERE last_visit < ".$last); $sth = $this->dbh->exec($sql); return $sth; } } ?>使用方法:
test.php
<?php require_once('session.php'); $session = new Session; session_start(); ?>类文件里有一个有些特别的地方:
//为保持客户端cookie生命期与服务端session生命期相同,进行写cookie操作
$sessionid = session_id();
$sessionname = session_name();
setcookie($sessionname, $sessionid, $this->lifeTime, '/', $this->domain);
如果只是单单将session.gc_maxlifetime和session.cookie_lifetime的设置成相同的时间,那么以后在每次session_start()的时候,服务端session的生存期都会自动得到延长,但是保存sessionid的cookie却没有。当然,也可以直接将session.cookie_lifetime设置成一个非常大的数,只是这样显得比较霸道。