PHP重写session机制
众所周知,session在web应用中占有举足轻重的地位。而且,在很多情况下我们需要改变session的存储位置。当然了,改变session存储的位置可以在php.ini文件中直接修改。但是,这需要我们对服务器有足够的权限。可是事实却是在很多时候我们并没有权限去操作php.ini文件的权限。这时需要我们通过PHP提供的session_set_save_handler()函数来重写session。
针对这一情况,PHP 5 > 5.4和php 7 支持SessionHandlerInterface 接口。
SessionHandlerInterface {
/* 方法 */
abstract public bool close ( void )
abstract public bool destroy ( string $session_id )
abstract public bool gc ( int $maxlifetime )
abstract public bool open ( string $save_path , string $name )
abstract public string read ( string $session_id )
abstract public bool write ( string $session_id , string $session_data )
}
我们需要做的就是实现这个接口中的所有的方法。然后通过session_set_save_handler()函数来使方法生效。
注:本文中的例子是将session存到redis中。对于PHP如何操redis,大家可以参考《PHP操作Redis的两种方式》。
下面我们来分别介绍这些函数的实现方法。
open函数
abstract public bool SessionHandlerInterface ::open ( string $save_path , string $name ){}
重新初始化现有的session,抑或是创建一个session。该函数在session_start()函数执行的时候被调用。
$save_path 这个参数对应的就是php.ini中的session.save_path选项。这个选项设置的值就是$save_path的值。默认情况下,php.ini中session.save_path这个选项是被注释的,所以$save_path的值为空。举个例子:session.save_path设置为/tmp ,则$save_path的值为/tmp。
$name 这个参数对应的就是php.ini中的 session.name选项。默认情况下session.name设置为PHPSESSID。 所以说$name参数值为PHPSESSID。
我在实现这个函数的时候没有做其他的处理(因为我想将session存到redis中),只是连接了redis数据库。
public function open($save_path, $name){
/*
* 首先连接服务器
*/
$this->parseConnect();
return true;
}
close函数
abstract public bool SessionHandlerInterface :: close(){}
关闭当前的session。该函数在当关闭session的时候被自动触发,或者在程序中调用session_write_close()函数是触发close()函数。
在实现该函数时没有做什么特殊的处理
public function close(){
return true;
}
read函数
abstract public bool SessionHandlerInterface :: read($session_id){}
读取session数据。当调用session_start()函数的时候会触发read()函数。当然该函数的触发是在open之后的。
$session_id 该参数就是对应的由客户端传过来的sessionId。所有的操作都需要根据这个sessionId来进行。
public function read($session_id){
/*
* 根据sessionId 构造键名
*/
$key = $this->prefix.':'.$session_id;
//读取当前sessionid下的data数据
$res = $this->handle->hGet($key,'data');
//读取完成以后 更新时间,说明已经操作过session
$this->handle->hSet($key,'last_time',time());
return $res;
}
write函数
abstract public bool SessionHandlerInterface :: write($session_id , $session_data){}
该函数是将session的数据写到相应的位置去。当操作$_SESSION来序列化数据的时候该函数被触发。
$session_id 这个参数就是我们上面所说的sessionId。
$session_data 是我们要存储的数据。这里需要说明一下,我们通过$_SESSION来设置数据的时候,第一次我们设置了$_SESSION[‘login’] = ‘ok’,这时$session_data的值为login|s:2:ok。write会把login|s:2:ok写入到redis中。然后我们不退出session,再次设置$_SESSION[‘name’] = ‘onmpw’。这时$session_data的值不只是name|s:5:onmpw,而是login|s:2:ok;name|s:5:onmpw。因为在write之前会先触发read来读取redis中的数据。读取到数据以后将这些数据连同通过$_SESSION[‘name’] = ‘onmpw’得到的值一块儿作为$session_data参数的值来进行更新。
public function write($session_id, $session_data){
/*
* 根据sessionId 构造键名
*/
$key = $this->prefix.':'.$session_id;
//查看该键内容是否存在
if(!$this->handle->exists($key)){
/*
* 不存在则插入新的内容
* 插入最后更新时间
*/
$this->handle->hset($key,'last_time',time());
}else{
/*
* 存在,则更新该键值
*/
$this->handle->hMset($key,array('last_time'=>time(),'data'=>$session_data));
}
return true;
}
destroy函数
abstract public bool SessionHandlerInterface ::destroy($session_id){}
当函数session_destroy()调用的时候触发该函数。这时我们可以在该函数中将$session_id对应的数据销毁掉
public function destroy($session_id){
/*
* 根据sessionId 构造键名
*/
$key = $this->prefix.':'.$session_id;
$this->handle->hDel($key,'data');
}
gc函数
abstract public bool SessionHandlerInterface ::gc($maxlifetime){}
清除垃圾session,也就是清除过期的session。该函数是基于php.ini中的配置选项:session.gc_divisor, session.gc_probability 和 session.gc_lifetime所设置的值的。
$maxlifetime 参数的值就是 session.gc_lifetime选项所设置的值。
这个函数是否被触发要取决于session.gc_divisor和session.gc_probability这两个选项。该函数被触发的概率为 session.gc_probability/session.gc_divisor。如果probability设置为1,divisor设置为100。那么gc函数被触发的概率就是1%。也就是说在100个请求中可能会在某一个请求过程中触发这个函数。从这里我们可以知道,如果客户端一直没有请求,那这个函数就永远不会被触发。即使有些session信息没被操作的时间已经超过了session.gc_lifetime所设置的时间。
那什么是过期的session呢?这么来说吧,假如session.gc_lifetime设置的值为30(默认单位为s 秒),一条session信息,从最后一次被操作的时间开始计时,如果在30秒内没有再被操作,那这条session就被定为垃圾信息了。当gc函数被触发的时候这条信息就被清除掉了。如果说,你在30秒内又对这条session信息进行了操作——即使是在29s的时候,那这条session信息会在你最近操作的这一时刻开始再重新计时30秒。
public function gc($maxlifetime){
/*
* 取出所有的 带有指定前缀的键
*/
$keys = $this->handle->keys($this->prefix.'*');
$now =time(); //取得现在的时间
foreach($keys as $key){
//取得当前key的最后更新时间
$last_time = $this->handle->hGet($key,'last_time');
/*
* 查看当前时间和最后的更新时间的时间差是否超过最大生命周期
*/
if(($now - $last_time) > $maxlifetime){
//超过了最大生命周期时间 则删除该key
$this->handle->del($key);
}
}
}
上面就是我们在重写session机制中会用到的几个函数。上面的部分代码是我写的将session保存到redis中的一个例子中的部分代码。大家可以从 https://github.com/onmpw/PHPLibrary/tree/master/Code/Session 查看完整代码。希望本文对大家有帮助。
相关文章
你必须知道的 PHP 中的内存管理知识
发布时间:2023/02/25 浏览次数:177 分类:编程语言
-
内存管理是 PHP 编程的一个重要方面,它可以极大地影响应用程序的性能和可伸缩性。 它涉及理解内存分配模型,通过高效的编程技术最大限度地减少内存使用,以及利用缓存和延迟加
PHP 通过引用与通过值传递变量
发布时间:2023/02/25 浏览次数:165 分类:编程语言
-
在 PHP 中,我们可以通过两种不同的方式将变量传递给函数:按值和按引用。 了解这两种传递变量的方法之间的区别对于编写高效且有效的 PHP 代码很重要。 按值传递变量 默认情况下,
详细介绍 PHP内存分配函数
发布时间:2023/02/25 浏览次数:164 分类:编程语言
-
PHP 是一种流行的服务器端脚本语言,广泛用于 Web 开发。 与大多数编程语言一样,PHP 需要分配内存来创建变量、数组、对象和其他数据结构。 PHP 提供了几个分配函数来动态分配内存。
PHP 内存分配类型:堆栈和堆
发布时间:2023/02/25 浏览次数:83 分类:编程语言
-
内存分配是编程的一个重要方面,它决定了程序如何管理和使用内存。 在 PHP 中,有两种主要的内存分配类型: 堆栈和堆 。 堆栈 堆栈是一种内存分配类型,用于存储局部变量和函数参
我们来看一下 PHP是如何分配内存的
发布时间:2023/02/25 浏览次数:195 分类:编程语言
-
PHP 使用内存管理系统,根据需要动态分配内存,并在不再使用时释放它。 这意味着您不需要在 PHP 脚本中手动分配或释放内存。 PHP 的内存管理系统基于 zend_mm_heap 的概念,它是一个内
PHP 有哪些内存优化技术
发布时间:2023/02/25 浏览次数:92 分类:编程语言
-
介绍 PHP 是一种流行的 Web 开发编程语言,但由于其动态特性和垃圾收集过程,它可能容易出现高内存使用率。 但是,我们可以使用多种技术来优化内存使用并提高 PHP 应用程序的性能。
我们看一下PHP内存泄漏的常见原因
发布时间:2023/02/25 浏览次数:74 分类:编程语言
-
在长时间运行的 PHP 应用程序中,内存泄漏可能是一个严重的问题。 随着时间的推移,内存泄漏会导致应用程序消耗越来越多的内存,直到它崩溃或变得无响应。 在本文档中,我们将探
PHP 中如何高效的使用内存
发布时间:2023/02/25 浏览次数:158 分类:编程语言
-
PHP 是一种用于构建 Web 应用程序的流行编程语言,高效的内存使用对于创建高性能应用程序至关重要。 在本文中,我们将探讨在 PHP 中高效使用内存的一些最佳实践。 1. 限制存储在内存