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 ...
随机推荐
- 处理通过<input type="file">的Post 请求
[HttpPost] public ActionResult Cal() { string ExcelName = System.DateTime.Now.ToString("yyyyMMd ...
- Underscore.js(1.7.0) 中文文档 Underscore.js 入门
原文地址:http://www.css88.com/doc/underscore/ Underscore.js 入门 http://www.tuicool.com/articles/jQ3IfeR
- socket 发送Ping包
参考链接: http://blog.csdn.net/zpxili/article/details/11542041 http://blog.csdn.net/cbuttonst/article/de ...
- C# 插入排序算法
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...
- python高级编程之我不测试
# -*- coding: utf-8 -*-__author__ = 'Administrator'#测试驱动开发(高级编程处学习,第11章)#测试驱动开发也叫TDD, 是制造高质量软件的一种简单技 ...
- C++内存分配的五种方法
在C++中,内存分成5个区,他们分别是堆.栈.自由存储区.全局/静态存储区和常量存储区. 栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清楚的变量的存储区.里面的变量通常是局部变量.函数参数 ...
- myeclipse自动补全设置
第一步: windows -->preference -->java -->editor -->content Assist --> auto activation -- ...
- oracle(天猫处方药留言sql)
" ?> .dtd" > <sqlMap namespace="TmallTcMessage"> <typeAlias alias ...
- windows程序消息机制(Winform界面更新有关)--转
1. Windows程序消息机制 Windows GUI程序是基于消息机制的,有个主线程维护着消息泵.这个消息泵让windows程序生生不息. Windows程序有个消息队列,窗体上的所有消息是这个队 ...
- ControlStyles(枚举)
指定控件的样式和行为. 此枚举有一个 FlagsAttribute 特性,通过该特性可使其成员值按位组合.属性: ContainerControl:如果为true,则控件是类似容器的控件. UserP ...