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将会话保存在数据库里的更多相关文章

  1. 解决secureCRT 数据库里没有找到防火墙 '无' 此会话降尝试不通过防火墙进行连接。

    解决secureCRT 数据库里没有找到防火墙 '无' 此会话降尝试不通过防火墙进行连接.的方法 中文版的CRT由于汉化的问题(把null翻译成无了),导致每次打开都会有个防火墙的错误提示:数据库里没 ...

  2. Django数据操作F和Q、model多对多操作、Django中间件、信号、读数据库里的数据实现分页

    models.tb.objects.all().using('default'),根据using来指定在哪个库里查询,default是settings中配置的数据库的连接名称. 外话:django中引 ...

  3. 解决secureCRT数据库里没有找到防火墙 '无'问题

    中文版的secureCRT由于汉化的问题(把null翻译成无了),导致每次打开都会有个防火墙的错误提示:数据库里没有找到防火墙 '无' 此会话将尝试不通过防火墙进行连接.出现这个错误的原因是在secu ...

  4. Java Hour 47 WeatherInfo 保存到数据库

    经历了上周简单的休整以后,我们继续Hibernate 之旅. 保存到数据库 private void saveWeatherInfo(Weatherinfo weatherInfo) { // Sav ...

  5. PHP SESSION 保存到数据库

    PHP SESSION 的工作原理 在客户端(如浏览器)登录网站时,被访问的 PHP 页面可以使用 session_start() 打开 SESSION,这样就会产生客户端的唯一标识 SESSION ...

  6. 解决secureCRT数据库里没有找到防火墙 '无'问题,转自:http://jingyan.baidu.com/article/9989c74601274bf649ecfe74.html

    中文版的secureCRT由于汉化的问题(把null翻译成无了),导致每次打开都会有个防火墙的错误提示:数据库里没有找到防火墙 '无' 此会话将尝试不通过防火墙进行连接.出现这个错误的原因是在secu ...

  7. thinkphp从数据库里的html代码显示页面不解析

    首先,这个问题不应该出现在这里,因为以前在用ThinkPHP3.1.2的时候,利用富文本编辑器保存文本后,直接从数据库里面取出的数据都能正常显示,改用ThinkPHP3.2.3之后,thinkphp从 ...

  8. 芝麻HTTP:Python爬虫实战之抓取爱问知识人问题并保存至数据库

    本次为大家带来的是抓取爱问知识人的问题并将问题和答案保存到数据库的方法,涉及的内容包括: Urllib的用法及异常处理 Beautiful Soup的简单应用 MySQLdb的基础用法 正则表达式的简 ...

  9. 解决SecureCRT中文版"数据库里没找到防火墙'无'"的错误提示

    问题描述: 最近从同事那拷贝到一个中文版的SecureCRT,但是每次打开都会有个防火墙的错误提示,“数据库里没找到防火墙“无”.此会话将尝试不通过防火墙进行连接. 出现这个错误的原因是在Secure ...

随机推荐

  1. Google GFS文件系统深入分析

    Google GFS文件系统深入分析 现在云计算渐成潮流,对大规模数据应用.可伸缩.高容错的分布式文件系统的需求日渐增长.Google根据自身的经验打造的这套针对大量廉价客户机的Google GFS文 ...

  2. CSS中display:block的使用介绍

    在CSS的规定中,每个页面元素都有一个display的属性,用于确定这个元素的类型是行内元素,还是块级元素: (1)行内元素:元素在一行内水平排列,依附于其他块级元素存在,因此,对行内元素设置高度.宽 ...

  3. puppetSvn集成

  4. python glob标准库基础学习

    #glob文件名模式匹配#作用:使用unix shell规则查找与一个模式匹配文件名"""尽管glob api很小,但这个模块很强大,只要程序需要查找文件系统中名字与某种 ...

  5. iOS开发:深入理解GCD 第一篇

    最近把其他书籍都放下了,主要是在研究GCD.如果是为了工作,以我以前所学的GCD.NSOperation等知识已经足够用了,但学习并不仅仅知识满足于用它,要知其然.并且知其所以然,这样才可以不断的提高 ...

  6. WebService- 使用 CXF 开发 SOAP 服务

    选框架犹如选媳妇,选来选去,最后我还是选了“丑媳妇(CXF)”,为什么是它?因为 CXF 是 Apache 旗下的一款非常优秀的 WS 开源框架,具备轻量级的特性,而且能无缝整合到 Spring 中. ...

  7. DirectX Sample-Blobs实现原理

    这个例子的实现主要包括两步: 1.计算三维采样坐标和color,实现代码是for( i = 0; i < NUM_Blobs; ++i )那个循环,计算完成以后g_pTexGBuffer[0]保 ...

  8. 信息熵(Entropy)究竟是用来衡量什么的?

    信息熵(Entropy)究竟是用来衡量什么的? ——与Philip ZHANG商榷 思明 Philip ZHANG先生在反驳彭小明的时候,提出一个观点,他说:“ 就语言文 字来说,总体效率不是用民族主 ...

  9. spring 中StoredProcedure的使用方法

    StoredProcedure是一个抽象类,必须写一个子类来继承它,这个类是用来简化JDBCTemplate运行存储过程操作的. 首先我们写一个实现类: package com.huaye.frame ...

  10. [CSAPP笔记][第六章存储器层次结构]

    第六章 存储器层次结构 在简单模型中,存储器系统是一个线性的字节数组,CPU能够在一个常数访问每个存储器位置. 虽然是一个行之有效的模型,但没有反应现代系统实际工作方式. 实际上,存储器系统(memo ...