前三篇简单的总结了下会话控制和文件操作,这一篇说说会话控制的自定义处理方式。既然知道了文件的基本读写,而且在会话控制中,也有人提到,session数据可以保存到缓存或数据库中,实际上当然不会是直接利用php的session处理机制,将所有用户的session信息保存报一个文件中,访问量大、信息数据多、无法共享等等问题可能会出现,因此,我们需要自定义会话控制。在实现自定义会话控制前,就要知道php本身是如何来做会话管理的,。这里,就简单的实现它被保存到自定义目录下的文件里边,那么缓存或数据库就很明显了。

首先看几个命令再说,这将有利于我们理解它的机制。打开php配置文件,找到一些session.打头的命令项:

session.save_handler:它的值默认是files,看它的英文解释可知,如果它的值是files,那么就是使用php本身的机制来处理,它的信息会保存到session.save_path所指定的地方,如果是想自定义session处理的话,需要将users赋给它。

session.save_path:在win版的php配置文件中,它默认是被注释掉的,即在配置文件中并没有说它的值是多少,但总得有个地方放session数据是不,win下,这个地方是C:\Windows\Temp,进去即可找到(如下,如果你练习过session存储的程序的话)以sess_为前缀、后面跟一长串长度一样的字符串、没有扩展名的文件,它就是默认的session数据了,里面存放的是序列化(也有的说是串行化)后的数据(如user|s:4:"Jack";,user是变量名,"Jack"是变量值,s表示变量类型string,冒号后边的4表示长度,最后的分号以结束这个变量,这只是简单的数值型,还有对象等等也可序列化)。关于save_path,他还有一些配置方式,比如按数字分级,更方便大数量的数据文件存放,具体不细说。

弄清楚它的处理及存放后,其实就应该冒出这样一个问题:我们平时又没动session数据,如果它就这么一直放着存着,那不会把磁盘占满吗?难道要专门写一个脚本定时来删除它们?幸运的是php提供了session的自动回收机制,即垃圾回收。了解回收机制还是需要看命令项。

session.gc_probability:gc是garbage collection回收简写,意思上是概率、可能性

session.gc_divisor:意思上是除数,这两项要综合起来看,在已有的session文件里边,对于过期的,以gc_probability/gc_divisor的概率来选择其中一个删除,在每次session初始化时都会这样做。所以假设gc_divisor是1000,gc_probability是1,而此时恰好有1000个过期的session文件,那么也不是全部删除,而是随机挑其中一个删除,即1/1000的概率。

session.gc_maxlifetime:session的有效时间,只有超过了这个有效时间的session文件才会被列入php垃圾回收的范围内。

了解了这些准备知识后,接下来就进入到session的自定义处理过程了。

不管是保存在memcache还是数据库,还是其他方式,最初的原理都一样,php在处理session数据时使用的是一个函数:session_set_save_handler。其最新的原型是:

bool session_set_save_handler ( callable $open , callable $close , callable $read , callable $write , callable $destroy , callable $gc [, callable $create_id])

可以看到这个函数又会去调用回调函数。首先,我们知道的是,从session的开始到挂掉,要session_start一个会话,要往$_SESSION里边写入数据保存session信息,或者读取该数组中的信息,用完时又会去destroy它的数据,这么几个步骤,对应这几个函数名一看,就八九不离十了。

open:它有两个参数---$save_path和$session_id,传递给它的参数已经表明,它是处理存放session数据的路径的,实际上就是通过它来改变文件的存放目录,就可以不是C:Windows\Temp。它在创建一个全新的会话session_start,或者start一个已经存在的会话时会被php内部去自动调用(session_start()),只有返回true才可以进行下一步处理。

close:它没有参数,作用是关闭当前会话,当你关闭一个会话时会自动调用,或者当你显式调用另一个php的函数session_write_close时。

read:它需要一个参数$session_id,作用是从存储session数据的地方读取id为$session_id的用户的session信息,并返回,以便后续的内部处理。当session_start()开启一个会话时,或者在内部调用open函数后会继续调用它。返回当前用户会话数据写入$_SESSION。

write:它需要两个参数---$session_id和$session_data,很明显,写入用户$session_id的对应的会话信息$session_data到指定文件。这个session_data是序列化后的数据,返回true可以进行下一步操作。它可以是正常的脚本被关闭时执行,也可是内部函数session_write_close()调用或者内部函数session_register_shutdown调用失败时执行。最明显的,当我们给$_SESSION数组赋值时它就会被执行。

destroy:它有一个参数$session_id,在session_destroy时会调用它,删除session_id对应的会话信息。

gc:参数是$maxlifetime,对于启动垃圾回收程序时(开始会话或者session_start被调用)执行,貌似启动垃圾回收时也是be randomly called,任性!

create_sid:这个回调函数是可选的,可以不写。它没有参数,当需要一个新的会话session id时可以写写,比如php还有一个重新生成会话id的函数session_regenerate_id。

总结下就是,session_start()时,要执行open、read、gc,在对$_SESSION数组赋值时,以后再操作$_SESSION不会调用它们,调用session_write_close内部函数时就会去调用write和close。

======================================================================

啰嗦了这么多,就是说,在start一个会话之前,我们要做的事是:

1、session.save_handler改为user,重启Apache,或者,使用ini_set临时修改命令项;

2、编写session_set_save_handler函数里对应的必选的回调函数

3、重新定义session_set_save_handler函数,使用上面的回调函数的名字;

4、code一个会话程序,在每一次调用session_start()之前,确保本次运行脚本时有上面的重定义函数可被调用到。

修改命令的工作已做,先编写一个脚本custom_session.php,自定义session_set_save_handler函数。

<?php
ini_set('save_handler', 'users'); // 使用自定义处理 $session_save_path = 'G:/sessionFiles/'; // 自定义一个存放目录 // session_start()会来调用它
function open($save_path, $session_id){
echo '<br>call open.<br>';
global $session_save_path;
$save_path = $session_save_path; //修改存放session数据的目录
return true;
} // 销毁session或调用session_write_close时调用
function close(){
echo '<br>call close.<br>';
return true;
} // 读取session数据并返回
function read($session_id){
echo '<br>call read.<br>';
global $session_save_path;
$filename = $session_save_path.'sessionId_'.$session_id.'.txt';
$content = @file_get_contents($filename);
return $content;
} // 将对应用户(以会话id判断)的数据写入文件
function write($session_id, $session_data){
echo '<br>call write.<br>';
global $session_save_path;
$filename = $session_save_path.'sessionId_'.$session_id.'.txt'; // 自定义一个文件名,最好有一定规则 if(($handle = @fopen($filename, 'w+')) == true){
$bytes = @file_put_contents($filename, $session_data);
return $bytes;
}
else{
return false;
}
return false;
} // 调用session_destroy时执行,删除对应
function destroy($session_id){
echo '<br>call destroy.<br>';
global $session_save_path;
$filename = $session_save_path.'sessionId_'.$session_id.'.txt';
return @unlink($filename); // 删除文件/数据
} // 垃圾回收
function gc(int $maxlifetime){
echo '<br>call gc.<br>';
global $session_save_path;
$files = glob($session_save_path.'sessionId_*');
foreach($files as $filename){
if(filemtime($filename) + $maxlifetime < time()){
@unlink($filename);
}
}
return true;
} session_set_save_handler('open', 'close', 'read', 'write', 'destroy', 'gc');

然后,写了个简单的用户登录验证的脚本来试试,前办部分是php验证操作,后边是页面,还要include前面的自定处理方式脚本

<?php
// 改变session为自定义处理方式
include 'custom_session.php'; echo '<br>before call session_start fun.<br>';
session_start(); //开启session会话,若已开启则返回已存在session id echo 'session id: '.session_id().'<br>'; //输出session id if(!empty($_POST["sub"])) //如果点击登录
{
include "conn.inc.php"; //连接数据库
$sql = "select id from sessionuser where username='".$_POST["user"]."' and password='".md5($_POST["pass"])."'";
//echo 'sql=> '.$sql.'<br>';
$result = $mysqli->query($sql); if($result->num_rows > 0)
{
$assRow = $result->fetch_assoc();
$_SESSION["user"] = $_POST["user"]; // 存储session数据
$_SESSION["uid"] = $assRow["id"];
$_SESSION["islogin"] = 1; }else{
echo "<br>wrong username or password, please relogin.";
}
} ?>
<html>
<head>
<title>Session Test</title>
</head>
<body>
<form action="login.php" method="post">
<table align="center" >
<caption><h3>ID login</h3></caption>
<tr>
<td>username</td>
<td><input type="text" name="user" /></td>
</tr>
<tr>
<td>password</td>
<td><input type="password" name="pass" /></td>
</tr>
<tr>
<td colspan="2" align="center">
<input type="submit" name="sub" value="login" />
</td>
</tr>
</table>
</form>
</body>
</html> <?php echo '<br>script end.<br>'; ?>

结果如下:

在session_start()前做了标记“before call session_start fun.”,在脚本最后做了标记“script end”,很清楚的看到,在session_start()调用随后就调了open和read,在脚本自动结束前会自动去调用write和close,这里并没有去销毁session信息,但是脚本结束时自动会调write写入session信息,close关闭当前会话。

然后是登出,销毁session信息:

<?php
// 自定义session处理方式
include 'custom_session.php'; echo '<br>before call session_start fun.<br>';
session_start(); $_SESSION = array(); //一次性删除存于$_SESSION中所有变量 if(isset( $_COOKIE[session_name()] ) )
{
setcookie(session_name(), '', time()-3600, '/');
} echo '<br>before call session_destroy fun.<br>';
session_destroy();
?> <br><a href="login.php">relogin</a><br>
<?php echo '<br>script end.<br>'; ?>

同样,在session_start处做了标记,在调用session_destroy出标记了“before call session_destroy fun.”,在脚本结束时标记“script end.”,看看效果:

可以看到,在调用session_start后紧接着是open和read函数,然后走销毁程序,在调用session_destroy后紧接着会去调destroy和close函数,然后才是脚本的结束script end,进一步证明了这些函数的调用情形。

最后,session数据是不是保存到了我们指定的地方呢?look ↓ 没跑

当然了,你也可以标记得更细致,来看看其中的道理~ note end

PHP之自定义会话控制---使用文件处理的更多相关文章

  1. Flask之项目创建,路由以及会话控制

    Flask Flask诞生于2010年,是Armin ronacher(人名)用 Python 语言基于 Werkzeug 工具箱编写的轻量级Web开发框架. Flask 本身相当于一个内核,其他几乎 ...

  2. PHP13 会话控制

    学习要点 会话控制使用的意义 用户跟踪方式 Cookie的设置.读取以及删除 Session的设置.读取以及删除 自定义session处理方式 会话控制 什么是会话控制 实现服务器跟踪同一个客户端的连 ...

  3. 会话控制:session与cookie

    我们在浏览网站时,访问的每一个web页面都需要使用"http协议"实现.而HTTP协议是无状态协议,就是说HTTP协议没有一个内建机制来维护两个事务之间的状态.当一个用户请求一个页 ...

  4. (实用篇)php通过会话控制实现身份验证实例

    会话控制的思想就是指能够在网站中根据一个会话跟踪用户.这里整理了详细的代码,有需要的小伙伴可以参考下. 概述 http 协议是无状态的,对于每个请求,服务端无法区分用户.PHP 会话控制就是给了用户一 ...

  5. PHP之会话控制小结

    会话控制是一种跟踪用户的通信方式,使用会话控制主要基于以下几点:由于http协议的无状态性,使得不能通过协议来建立两次请求之间的关联:对于通常的页面之间的数据传递方式get和post而言,主要处理参数 ...

  6. php——会话控制

    1.什么叫做会话控制 允许服务器根据客户端做出的连续请求. 2.为什么需要会话控制? 因为当你打开一个网站,并想访问该网站的其他页面的时候,如果没有会话控制,当跳转到其他页面的 时候,就需要再次输入账 ...

  7. PHP中的会话控制

    了解HTTP(超文本传输协议)可以知道,它采用请求与响应的模式,最大的特点就是无连接无状态. 无连接:每次连接仅处理一个客户端的请求,得到服务器响应后,连接就结束了 无状态:每个请求都是独立的,服务器 ...

  8. 理解PHP中的会话控制

    会话控制是一种跟踪用户的通信方式,使用会话控制主要基于以下几点:由于http协议的无状态性,使得不能通过协议来建立两次请求之间的关联:对于通常的页面之间的数据传递方式get和post而言,主要处理参数 ...

  9. cookie、localStorage、sessionStorage和会话控制机制

    简介 cookie cookie的内容主要包括:名字Name.值Value.域Domain.路径Path.过期时间Expires/Max-Age.大小Size.HTTP.Secure.SameSite ...

随机推荐

  1. 解北大OJ1088滑雪问题的记录

    问题: Time Limit:1000MS   Memory Limit:65536K Total Submissions:67600   Accepted:24862 Description Mic ...

  2. Unity3D之Mecanim动画系统学习笔记(四):Animation State

    动画的设置 我们先看看Animation Clip的一些设置: Loop time:动画是否循环播放. 下面出现了3个大致一样的选项: Root Transform Rotation:表示为播放动画的 ...

  3. Protobuf一键生成代码bat文件

    最近在摆弄Unity的Socket,需要用到Protobuf,一般都会有多个协议文件,所以研究了下bat的批处理,下面给出批处理文件代码: @echo off ::协议文件路径, 最后不要跟“\”符号 ...

  4. MVC神韵---你想在哪解脱!(十三)

    维护模型与数据库结构之间的差别 现在我们已经将应用程序修改完毕,在Movie数据模型中添加了一个Rating属性.现在让我们重新运行应用程序,打开“http://localhost:xx/Movies ...

  5. 学习LINQ,发现一个好的工具。LINQPad!!

    今日学习LINQ,发现一个好的工具.LINQPad!! 此工具的好处在于,不需要在程序内执行,直接只用工具测试.然后代码通过即可,速度快,简洁方便. 可以生成其LINQ查询对应的lambda和SQL语 ...

  6. 前端响应式设计中@media等的相关运用

    现在做前端响应式网站特别,响应式成为现在前端设计一个热点,它成为热点的最主要的原因就是,移动端设备屏幕的种类多样,那么如何设置响应式屏幕. /*打印样式*/ @mediaprint{color:red ...

  7. 什么是比特币(bitcoin)

    一.什么是比特币? 比特币是一种由开源的P2P软件产生的电子货币,是一种网络虚拟货币.比特币使用遍布整个P2P网络节点的分布式数据库来记录货币的交易,并使用密码学的设计来确保货币流通各个环节安全性.比 ...

  8. 节点文件将两个不同格式的XML文件,进行节点对照,并生成一个用于对照功能的XML

    本文纯属个人见解,是对前面学习的总结,如有描述不正确的地方还请高手指正~ 经常有的需求是,需要将一种格式的XML转换成另一种XML.如果要实现这个功能首先需要将两个不同XML手动建立节点对比关系.然后 ...

  9. java搭建finagle(1)

    1.新建maven项目 2.pom文件添加依赖 添加3个主要依赖<dependency> <groupId>com.twitter</groupId> <ar ...

  10. Codeforces Beta Round #85 (Div. 1 Only) B. Petya and Divisors 暴力

    B. Petya and Divisors Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/111 ...