php将会话保存在数据库里
php默认把会话保存在临时文件中,保存在数据库中可以提高安全性,在共享主机服务器上,所有web站点都使用同一个临时目录,这意味着数十个程序都在同一位置进行文件读取的操作,我们很容易就编写一个脚本从这个目录里读取全部文件的数据。(在共享主机,涉及安全的另一个技巧是改变会话目录,session_save_path)。
另外,把会话数据保存在数据库还有利于更方便检索web站点会话的更多信息,我们可以查询活动会话的数量,还可以对会话数据进行备份。
把会话数据保存在数据库的第3个原因是,如果站点运行在多个服务器,同一个用于在一个会话过程中可能对不同服务器上的多个页面产生请求,而会话数据如果保存在某台服务器上的文件里,就不能被其他服务器上的页面所使用。这不是大多数开发者可能会面临的状态,但如果遇到这种情况,除了使用数据库保存会话数据之外没有其他的解决办法。
1.创建sessions表。
sessions表至少要有3个字段:

id为会话id,data为会话数据,last_accessed为会话数据最后一次访问时间。
2.定义会话函数,让php使用这些函数。
bool session_set_save_handler ( callable $open , callable $close , callable $read ,callable $write , callable $destroy , callable $gc )
open(string $savePath, string $sessionName)
The open callback works like a constructor in classes and is executed when the session is being opened. It is the first callback function executed when the session is started automatically or manually with session_start(). Return value is TRUE for success, FALSE for failure.
close()
The close callback works like a destructor in classes and is executed after the session write callback has been called. It is also invoked when session_write_close() is called. Return value should be TRUE for success, FALSE for failure.
read(string $sessionId)
The read callback must always return a session encoded (serialized) string, or an empty string if there is no data to read.
This callback is called internally by PHP when the session starts or when session_start() is called. (session_start()执行了调用了这个read函数)Before this callback is invoked PHP will invoke the open callback.
The value this callback returns must be in exactly the same serialized format that was originally passed for storage to the write callback. The value returned will be unserialized automatically by PHP and used to populate the$_SESSION superglobal. While the data looks similar to serialize() please note it is a different format which is speficied in the session.serialize_handler ini setting.
write(string $sessionId, string $data)
The write callback is called when the session needs to be saved and closed. This callback receives the current session ID a serialized version the $_SESSION superglobal. The serialization method used internally by PHP is specified in the session.serialize_handler ini setting.
The serialized session data passed to this callback should be stored against the passed session ID. When retrieving this data, the read callback must return the exact value that was originally passed to the write callback.
This callback is invoked when PHP shuts down or explicitly when session_write_close() is called. Note that after executing this function PHP will internally execute the close callback.
Note:
The "write" handler is not executed until after the output stream is closed. Thus, output from debugging statements in the "write" handler will never be seen in the browser. If debugging output is necessary, it is suggested that the debug output be written to a file instead.
destroy($sessionId)
This callback is executed when a session is destroyed with session_destroy() or with session_regenerate_id() with the destroy parameter set to TRUE. Return value should be TRUE for success, FALSE for failure.
gc($lifetime)
The garbage collector callback is invoked internally by PHP periodically in order to purge old session data. The frequency is controlled by session.gc_probability and session.gc_divisor. The value of lifetime which is passed to this callback can be set in session.gc_maxlifetime. Return value should be TRUE for success, FALSE for failure.
过程式脚本:
<?php # Script 3.1 - db_sessions.inc.php /*
* This page creates the functional
* interface for storing session data
* in a database.
* This page also starts the session.
*/ // Global variable used for the database
// connections in all session functions:
$sdbc = NULL; // Define the open_session() function:
// This function takes no arguments.
// This function should open the database connection.
function open_session() { global $sdbc; // Connect to the database.
$sdbc = mysqli_connect ('localhost', 'root', 'sm159357', 'yii_blog') OR die ('Cannot connect to the database.'); return true; } // End of open_session() function. // Define the close_session() function:
// This function takes no arguments.
// This function closes the database connection.
function close_session() { global $sdbc; return mysqli_close($sdbc); } // End of close_session() function. // Define the read_session() function:
// This function takes one argument: the session ID.
// This function retrieves the session data.
function read_session($sid) { global $sdbc; // Query the database:
$q = sprintf('SELECT data FROM sessions WHERE id="%s"', mysqli_real_escape_string($sdbc, $sid));
$r = mysqli_query($sdbc, $q); // Retrieve the results:
if (mysqli_num_rows($r) == 1) { list($data) = mysqli_fetch_array($r, MYSQLI_NUM); // Return the data:
return $data; } else { // Return an empty string.
return '';
} } // End of read_session() function. // Define the write_session() function:
// This function takes two arguments:
// the session ID and the session data.
function write_session($sid, $data) { global $sdbc; // Store in the database:
$q = sprintf('REPLACE INTO sessions (id, data) VALUES ("%s", "%s")', mysqli_real_escape_string($sdbc, $sid), mysqli_real_escape_string($sdbc, $data));
$r = mysqli_query($sdbc, $q); return mysqli_affected_rows($sdbc); } // End of write_session() function. // Define the destroy_session() function:
// This function takes one argument: the session ID.
function destroy_session($sid) { global $sdbc; // Delete from the database:
$q = sprintf('DELETE FROM sessions WHERE id="%s"', mysqli_real_escape_string($sdbc, $sid));
$r = mysqli_query($sdbc, $q); // Clear the $_SESSION array:
$_SESSION = array(); return mysqli_affected_rows($sdbc); } // End of destroy_session() function. // Define the clean_session() function:
// This function takes one argument: a value in seconds.
function clean_session($expire) { global $sdbc; // Delete old sessions:
$q = sprintf('DELETE FROM sessions WHERE DATE_ADD(last_accessed, INTERVAL %d SECOND) < NOW()', (int) $expire);
$r = mysqli_query($sdbc, $q); return mysqli_affected_rows($sdbc); } // End of clean_session() function. # **************************** #
# ***** END OF FUNCTIONS ***** #
# **************************** # // Declare the functions to use:
session_set_save_handler('open_session', 'close_session', 'read_session', 'write_session', 'destroy_session', 'clean_session'); // Make whatever other changes to the session settings. // Start the session:
session_start(); ?>
注意write_session的sql用到了replace into;
作用是没有就insert,有就update。
clean_session就是gc垃圾收集,参数$expire, DATE_ADD(last_accessed,INTERVAL $expire SECOND<NOW();接收以秒为单位的时间,用以决定什么是“旧”会话。删除查过指定时间没有被访问过的会话。
在session_start()之前必须调用session_set_save_handler。
使用新会话处理程序
众所周知,php在脚本呢运行时自动关闭数据库连接,对于结下了的范例脚本,这意味着在脚本运行之后,数据库连接是自动关闭的,然后会话函数会尝试向数据库写入数据并关闭连接,这样会导致产生一些莫名其妙的错误(比如“我的数据库连接去哪儿了?”),为了避免这一系列问题,我们应该在脚本结束之前调用session_write_close()函数,他会调用“写入”和“关闭”函数,而这时数据库连接还是有效的。
<?php # Script 3.2 - sessions.php /* This page does some silly things with sessions.
* It includes the db_sessions.inc.php script
* so that the session data will be stored in
* a database.
*/ // Include the sessions file:
// The file already starts the session.
require_once('db_sessions.inc.php');
?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
<title>DB Session Test</title>
</head>
<body>
<?php // Store some dummy data in the session,
// if no data is present.
if (empty($_SESSION)) { $_SESSION['blah'] = 'umlaut';
$_SESSION['this'] = 3615684.45;
$_SESSION['that'] = 'blue'; // Print a message indicating what's going on:
echo '<p>Session data stored.</p>'; } else { // Print the already-stored data.
echo '<p>Session Data Exists:<pre>' . print_r($_SESSION, 1) . '</pre></p>';
} // Log the user out, if applicable:
if (isset($_GET['logout'])) { session_destroy();
echo '<p>Session destroyed.</p>'; } else { // Print the "Log Out" link:
echo '<a href="sessions.php?logout=true">Log Out</a>';
} // Print out the session data:
echo '<p>Session Data:<pre>' . print_r($_SESSION, 1) . '</pre></p>'; ?>
</body>
</html>
<?php session_write_close(); ?>
不加session_write_close()会报错。
------------------------------------------
我前面写过一篇关于PHP SESSION NAME与SESSION ID关系的文章《SESSION NAME引用SESSION ID的问题(Discuz! 用户(自动)登录原理)》,
有兴趣的朋友可以看一下。我在里面提了一下SESSION在服务器端的存储方式问题,这里我们就使用session_set_save_handler 来实现
SESSION在服务器端的“另存为”。(关于session_set_save_handler的用法参看官网)
我们首先写一个存储类(注意这里没有对DB操作进行封装),暂叫session.class.php:
<?php
class session_mysql
{
private $_db_link;
private $_table;
//这个是SESSION的回收周期(秒)
private $_gc_lifetime = 30;
public function __construct($host, $user, $pwd, $db, $table = 'session', $sessionName = '', $cookieLife = 0)
{
$this->_db_link = mysql_connect ( $host, $user, $pwd );
if (! $this->_db_link)
{
return False;
}
if (mysql_query ( "USE $db" ))
{
$this->_table = $table;
session_set_save_handler ( array (&$this, 'open' ), array (&$this, 'close' ), array (&$this, 'read' ), array (&$this, 'write' ), array (&$this, 'destroy' ), array (&$this, 'gc' ) ); //这个函数里面每个参数都是字符串类型
//周期
$cookieLife = intval ( $cookieLife );
if ($cookieLife > 0)
{
session_set_cookie_params ( $cookieLife );
}
if ($this->_gc_lifetime > 0)
{
ini_set ( 'session.gc_maxlifetime', $this->_gc_lifetime );
} else
{
$this->_gc_lifetime = ini_get ( 'session.gc_maxlifetime' );
}
//名称
if (! empty ( $sessionName ))
{
ini_set ( 'session.name', $sessionName );
}
return session_start ();
}
return False;
}
public function open($save_path, $session_name)
{
return true;
}
public function close()
{
//删除过期的SESSION
$this->gc ( $this->_gc_lifetime );
//关闭数据库
//(注意如果系统其它功能和SESSION共用一个数据库,此处关闭可能会影响到其它功能,根据实际情况而定)
return mysql_close ( $this->_db_link );
}
public function read($id)
{
$id = mysql_real_escape_string ( $id );
$sql = "select `data` from `$this->_table` where `id`='$id' limit 1";
if ($result = mysql_fetch_assoc ( mysql_query ( $sql, $this->_db_link ) ))
{
return $result ['data'];
}
return '';
}
public function write($id, $data)
{
$time = time ();
$id = mysql_real_escape_string ( $id );
$data = mysql_real_escape_string ( $data );
$sql = "replace into `$this->_table` values('$id','$data',$time)";
return mysql_query ( $sql, $this->_db_link );
}
public function destroy($id)
{
$sql = "delete from `$this->_table` where `id`='$id' limit 1";
return mysql_query ( $sql, $this->_db_link );
}
public function gc($lifetime)
{
$expire = time () - $lifetime;
$sql = "delete from `$this->_table where `created_time` < $expire";
return mysql_query ( $sql, $this->_db_link );
}
}
接着我们写一个公用的方法,暂叫function.php:
<?php
function my_session_start($sessionName = '', $lifetime = 0)
{
//这里装载SESSION类(由于这里方便演示,文件结构分布不严谨)
require_once './session.class.php';
//配置好相关内容
$session = new session_mysql ( '127.0.0.1', 'xxx', 'xxx', 'test', 'session', $sessionName, $lifetime );
if ($session)
{
return True;
}
return False;
}
最后,我们来测试一下效果,test.php:
<?php
//测试一下
require_once './function.php';
error_reporting ( E_ALL );
my_session_start ();
$_SESSION ['test'] = 'test';
$_SESSION ['testtwo'] = array ('testtwo' );
我们再在另外一个页面输出SESSION,test2.php
<?php
require_once './function.php';
error_reporting ( E_ALL );
my_session_start ();
var_dump($_SESSION);
运行后我们可以看到:
array 'test' =>string'test'(length=4) 'testtwo' =>array 0 =>string'testtwo'(length=7)
恩,一切正常。我们再看一下数据库中是什么:

恩,浏览器里又是怎么样的呐(这里只测试了FIREFOX,在生产时请务必各浏览器都要测试一下)?

恩,好了以就是一个简单的例子,那么如果我们要使用特殊的SESSION NAME或者要求有过期时间呐?test.php
<?php
//测试一下特殊名称
require_once './function.php';
error_reporting ( E_ALL );
my_session_start ( 'testhehe', 60 );//一分钟
$_SESSION ['lifettimetest'] = 'ok';
恩,我们来获取看看:test2.php
<?php
//测试一下特殊名称
require_once './function.php';
error_reporting ( E_ALL );
my_session_start ( 'testhehe' );
var_dump ( $_SESSION );
恩,我们来输出:
array 'lifettimetest' =>string'ok'(length=2)恩,貌似正常,我们再看看数据库与浏览器里的值:(数据库)

浏览器:

这样看来我们测试成功了。好了,以上就是关于session_set_save_handler的操作方法了。恩,如果你是还对PHP SESSION 原理不太熟悉,
请参看:《SESSION NAME引用SESSION ID的问题(Discuz! 用户(自动)登录原理)》
转自:http://blog.sina.com.cn/s/blog_79d4a75e010190g7.html
更多:
http://www.cnblogs.com/xingmeng/p/3223043.html
http://blog.snsgou.com/post-472.html
php将会话保存在数据库里的更多相关文章
- 解决secureCRT 数据库里没有找到防火墙 '无' 此会话降尝试不通过防火墙进行连接。
解决secureCRT 数据库里没有找到防火墙 '无' 此会话降尝试不通过防火墙进行连接.的方法 中文版的CRT由于汉化的问题(把null翻译成无了),导致每次打开都会有个防火墙的错误提示:数据库里没 ...
- Django数据操作F和Q、model多对多操作、Django中间件、信号、读数据库里的数据实现分页
models.tb.objects.all().using('default'),根据using来指定在哪个库里查询,default是settings中配置的数据库的连接名称. 外话:django中引 ...
- 解决secureCRT数据库里没有找到防火墙 '无'问题
中文版的secureCRT由于汉化的问题(把null翻译成无了),导致每次打开都会有个防火墙的错误提示:数据库里没有找到防火墙 '无' 此会话将尝试不通过防火墙进行连接.出现这个错误的原因是在secu ...
- Java Hour 47 WeatherInfo 保存到数据库
经历了上周简单的休整以后,我们继续Hibernate 之旅. 保存到数据库 private void saveWeatherInfo(Weatherinfo weatherInfo) { // Sav ...
- PHP SESSION 保存到数据库
PHP SESSION 的工作原理 在客户端(如浏览器)登录网站时,被访问的 PHP 页面可以使用 session_start() 打开 SESSION,这样就会产生客户端的唯一标识 SESSION ...
- 解决secureCRT数据库里没有找到防火墙 '无'问题,转自:http://jingyan.baidu.com/article/9989c74601274bf649ecfe74.html
中文版的secureCRT由于汉化的问题(把null翻译成无了),导致每次打开都会有个防火墙的错误提示:数据库里没有找到防火墙 '无' 此会话将尝试不通过防火墙进行连接.出现这个错误的原因是在secu ...
- thinkphp从数据库里的html代码显示页面不解析
首先,这个问题不应该出现在这里,因为以前在用ThinkPHP3.1.2的时候,利用富文本编辑器保存文本后,直接从数据库里面取出的数据都能正常显示,改用ThinkPHP3.2.3之后,thinkphp从 ...
- 芝麻HTTP:Python爬虫实战之抓取爱问知识人问题并保存至数据库
本次为大家带来的是抓取爱问知识人的问题并将问题和答案保存到数据库的方法,涉及的内容包括: Urllib的用法及异常处理 Beautiful Soup的简单应用 MySQLdb的基础用法 正则表达式的简 ...
- 解决SecureCRT中文版"数据库里没找到防火墙'无'"的错误提示
问题描述: 最近从同事那拷贝到一个中文版的SecureCRT,但是每次打开都会有个防火墙的错误提示,“数据库里没找到防火墙“无”.此会话将尝试不通过防火墙进行连接. 出现这个错误的原因是在Secure ...
随机推荐
- HTTP请求的TCP瓶颈分析
这篇文章基本是对<Web性能权威指南>第一章和第二章的读书笔记,另外加一些扩展内容,这本书确实赞,推荐 针对三次握手.流量控制(接收窗口).慢启动(cwnd,拥塞窗口).队首阻塞等方面看下 ...
- day52
今天依旧 阅读2篇的整理 数学试卷一套 综合练习计算 政治视频一个 范帅长难句一个 翻译两句 作文大小各一个 linux基础 今天折腾最久的应该是linux了 自己重新手动安装了一下 遇到的不少问题在 ...
- <php>json小结
1.页面中如果即用到jquery包,又用到js文件,要先写jquery包,再加载js文件 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Tr ...
- Hive 6、Hive DML(Data Manipulation Language)
DML主要是对Hive 表中的数据进行操作的(增 删 改),但是由于Hadoop的特性,所以单条的修改.删除,其性能会非常的低所以不支持进行级操作: 主要说明一下最常用的批量插入数据较为常用的方法: ...
- vagrant拷贝后vagrant file需要加的配置
config.ssh.forward_agent config.ssh.username = "vagrant" config.ssh.password = "vagra ...
- [hihocoder 1033]交错和 数位dp/记忆化搜索
#1033 : 交错和 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描写叙述 给定一个数 x,设它十进制展从高位到低位上的数位依次是 a0, a1, ..., an - 1 ...
- [RxJS] Creating Observable From Scratch
Get a better understanding of the RxJS Observable by implementing one that's similar from the ground ...
- javascript-Blob文件对象
一个Blob对象就是一个包含有只读原始数据的类文件对象.Blob对象中的数据并不一定得是JavaScript中的原生形式.File接口基于Blob,继承了Blob的功能,并且扩展支持了用户计算机上的本 ...
- Git远程使用技巧
git作为强大的版本管理软件,已经得到了广泛的应用,很多人对于本地的git操作已经非常熟悉了.然而有的时候,我们也需要一个远程的,类似云的仓库来存储我们的一些代码.github给予了我们不限量的空间来 ...
- Access restriction: The type * is not accessible due to restrict,报错问题
解决方案1: Eclipse 默认把这些受访问限制的API设成了ERROR. Windows -> Preferences -> Java -> Compiler -> Er ...