博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
详解Session
阅读量:6938 次
发布时间:2019-06-27

本文共 6076 字,大约阅读时间需要 20 分钟。

1 Session的基本概念和设置

Session存储在服务端,本质上和Cookie没有区别,都是针对http协议的局限性而提出的一种保持客户端和服务端间会话状态的机制。Session经常用来网站的上下文间实现页面变量的传递,用户身份认证,程序状态记录等。常见的有配合cookie使用,实现保存用户的登陆状态,或者记录用户的购物下单信息等。

在使用session之前必须先开启session,可使用session_start()开启session,同cookie一样,在开始之前不能有任何输出内容,否则会出现如下警告:

Warning: session_start(): Cannot send session cookie - headers already sent

也可以修改php.ini中的session.auto_start = 0 为 session.auto_start = 1,设置自动开启session支持,这样就不必每次在使用session的时候都要加上session_start()了。

Session的设置非常简单,可以直接使用$_SESSION[key]=value 的形式进行设置,其中key表示session的键,所有设置的session都存储在全局数组$_SESSION中。当在代码中设置了session时,在http请求的消息头中会携带一个名为PHPSESSID的cookie,其值是一个32位16进制的字符串。每个客户端向服务器请求时都会产生一个不同的值,如果清除掉浏览器的cookie,再次刷新页面将会重新设置一个PHPSESSID的值。服务端接收到这个cookie,根据其值在服务器中找到对应的session文件,从而实现保持与客户端链接状态的信息,其中session中存储着序列化的session键值等信息。设置了session的http请求消息头如下:

Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,/;q=0.8

Accept-Encoding:gzip, deflate, sdch, br

Accept-Language:zh-CN,zh;q=0.8

Cache-Control:max-age=0

Connection:keep-alive

Cookie:PHPSESSID=4680c9df2ce9ac4d1aa7f366bd92d83a

Host:localhost

Upgrade-Insecure-Requests:1

User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36

2 Session的工作原理和存储机制

前文说到,session是通过一个名为PHPSESSID的cookie来和服务器取得联系的,session通过sessionID(即PHPSESSID的值)来找到对应服务器中session的文件名的。sessionID时在客户端和服务端是通过 HTTP Requset 和 HTTP Response 传来传去的。sessionID按照一定的算法生成,保证其值的唯一性和随机性。Cookie里存储着session的sessionID和session的生存期,如果没有设置session的生存期,则sessionID存储在内存中,关闭浏览器时session失效,重新请求页面时回重新注册一个sessionID。

默认情况下,Session是存储在服务器硬盘上的,在php.ini可通过session.save_path设置session文件的存储路径,默认为服务器上/tmp目录。此配置指令还有一个可选的 N 参数来决定会话文件分布的目录深度。例如,设定为 '5;/tmp' 将使创建的会话文件和路径类似于 /tmp/4/b/1/e/3/sess_4b1e384ad74619bd212e236e52a5a174If。要使用 N 参数,必须在使用前先创建好这些目录。在 ext/session 目录下有个小的 shell 脚本名叫 mod_files.sh,windows 版本是 mod_files.bat 可以用来做这件事。此外注意如果使用了 N 参数并且大于 0,那么将不会执行自动垃圾回收。文件储存模块默认使用 mode 600 创建文件。通过 修改可选参数 MODE 来改变这种默认行为: N;MODE;/path ,其中 MODE 是 mode 的八进制表示。使用以上描述的可选目录层级参数 N 时请注意,对于绝大多数站点,大于1或者2的值会不太合适——因为这需要创建大量的目录:例如,值设置为 3 需要在文件系统上创建 64^3 个目录,将浪费很多空间和 inode。仅仅在绝对肯定站点足够大时,才可以设置 N 大于2。一个session文件的内容如下:

siteadmin_username|s:7:"special";siteadmin_truename|s:6:"特殊";siteadmin_usertype|i:1;

内容的格式为:session名|值类型:长度:值; 。

3 使用Redis存储Session

对于大访问量的网站来说,会有许多的客户端和服务端建立链接,那么将会生成许多的session文件,由于session文件是存储在硬盘上的,每次服务器去读取这些session文件都要经过许多的I/O操作。PHP中可使用session_set_save_handle()函数自定义session保存函数(如打开,关闭,写入,读取等)。session_set_save_handle()语法如下:

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

如果想使用 PHP 内置的会话存储机制之外的方式, 可以使用本函数。 例如,可以自定义会话存储函数来将会话数据存储到数据库。该函数的参数解析如下。

open(string $savePath, string $sessionName):open 回调函数类似于类的构造函数, 在会话打开的时候会被调用。 这是自动开始会话或者通过调用 session_start() 手动开始会话 之后第一个被调用的回调函数。 此回调函数操作成功返回 TRUE,反之返回 FALSE。

close():close 回调函数类似于类的析构函数。 在 write 回调函数调用之后调用。 当调用 session_write_close() 函数之后,也会调用 close 回调函数。 此回调函数操作成功返回 TRUE,反之返回 FALSE。

read(string $sessionId):如果会话中有数据,read 回调函数必须返回将会话数据编码(序列化)后的字符串。 如果会话中没有数据,read 回调函数返回空字符串。在自动开始会话或者通过调用 session_start() 函数手动开始会话之后,PHP 内部调用 read 回调函数来获取会话数据。 在调用 read 之前,PHP 会调用 open 回调函数。read 回调返回的序列化之后的字符串格式必须与 write 回调函数保存数据时的格式完全一致。 PHP 会自动反序列化返回的字符串并填充 $_SESSION 超级全局变量。 虽然数据看起来和 serialize() 函数很相似, 但是需要提醒的是,它们是不同的。

write(string $sessionId, string $data):在会话保存数据时会调用 write 回调函数。 此回调函数接收当前会话 ID 以及 $_SESSION 中数据序列化之后的字符串作为参数。 序列化会话数据的过程由 PHP 根据 session.serialize_handler 设定值来完成。序列化后的数据将和会话 ID 关联在一起进行保存。 当调用 read 回调函数获取数据时,所返回的数据必须要和 传入 write 回调函数的数据完全保持一致。PHP 会在脚本执行完毕或调用 session_write_close() 函数之后调用此回调函数。 注意,在调用完此回调函数之后,PHP 内部会调用 close 回调函数。

PHP 会在输出流写入完毕并且关闭之后 才调用 write 回调函数, 所以在 write 回调函数中的调试信息不会输出到浏览器中。 如果需要在 write 回调函数中使用调试输出, 建议将调试输出写入到文件。

destroy($sessionId):当调用 session_destroy() 函数, 或者调用 session_regenerate_id() 函数并且设置 destroy 参数为 TRUE 时, 会调用此回调函数。此回调函数操作成功返回 TRUE,反之返回 FALSE。

gc($lifetime):为了清理会话中的旧数据,PHP 会不时的调用垃圾收集回调函数。 调用周期由 session.gc_probability 和 session.gc_divisor 参数控制。 传入到此回调函数的 lifetime 参数由 session.gc_maxlifetime 设置。 此回调函数操作成功返回 TRUE,反之返回 FALSE。

create_sid():当需要新的会话 ID 时被调用的回调函数。 回调函数被调用时无传入参数, 其返回值应该是一个字符串格式的、有效的会话 ID。

一个关于使用Redis代替文件存储session的例子如下:

首先编写一个管理session的类sessionmanager,代码如下:

redis = new Redis();$this->redis->connect('10.116.19.14',6400);$reval = session_set_save_handler(array($this,"open"),array($this,"close"),array($this,"read"),array($this,"write"),array($this,"destroy"),array($this,"gc"));session_start();}public function open($patn,$name){return true;}public function close(){return true;}public function read($id){$value = $this->redis->get($id);if($value) {return $value;} else {return false;}}public function write($id,$data){if($this->redis->set($id,$data)) {$this->redis->expire($id,60);return true;} else {return false;}}public function destroy($id) {if($this->redis->delete($id)) {return true;}return false;}public function gc($maxlifetime){return true;}public function __destruct(){session_write_close();// TODO: Implement __destruct() method.}}?>

在该类的构造函数中,使用session_set_save_handler()设置session的处理函数,实例化该类时便完成了用指定函数接管系统处理session的工作。将以上代码保存为sessionmanager.php文件。在write回调函数中,以传入的sessionID作为key,以session的值作为redis中key的值存入redis,并设置过期时间为60秒;read方法以传入的sessionID为key从redis取出相应的session的值。destroy可根据传入的sessionID删除redis中的session。

我们编写另外一个设置session的脚本,并引入sessionmanager.php文件,示例化sessionmanager类。代码如下:

1,2,3,4,4);?>

保存以上代码为set.php,另外编写一个可访问session的脚本,代码如下:

保存以上代码为get.php文件。测试时,先访问set.php,然后再访问get.php,会在浏览器输出以下结果:

array(4) { ["namehaha"]=> string(10) "lixiaolong" ["namehah"]=> string(10) "lixiaolong" ["namehaa"]=> string(10) "lixiaolong" ["namhaha"]=> array(5) { ["a"]=> int(1) [0]=> int(2) [1]=> int(3) [2]=> int(4) [3]=> int(4) } }

可见已经成功的设置并获得了session。查看redis中存储的session信息,

redis 127.0.0.1:6400> get ruevh62hlm809d1p2lg2o0fbv7

“namehaha|s:10:"lixiaolong";namehah|s:10:"lixiaolong";namehaa|s:10:"lixiaolong";namhaha|a:5:{s:1:"a";i:1;i:0;i:2;i:1;i:3;i:2;i:4;i:3;i:4;}"

Redis中是以string的数据类型存储session的,其key遍是sessionID,也是HTTP Request中的cookie名为PHPSESSID的值。session在redis和在文件中的存储形式都是一样的,只不过在redis对双引号做了转义。

本文节选自 《php7实践指南》 陈小龙著

微信扫一扫,发现更多内容

761c57ad902ff50c.jpg

转载地址:http://cjbnl.baihongyu.com/

你可能感兴趣的文章
5: EL 表达式小结
查看>>
[数组]数组元素分割
查看>>
今日学习20190427
查看>>
HIbernate小结
查看>>
iOS开发-数据存储
查看>>
iOSUI-UIScrollView属性,方法大全
查看>>
2015大连华信校园招聘面试题--堆栈
查看>>
常用架构选型
查看>>
POJ 2533 Longest Ordered Subsequence
查看>>
机器学习入门-提取文章的主题词 1.jieba.analyse.extract_tags(提取主题词)
查看>>
一、javaSE (五)方法和数组知识
查看>>
关于如何在虚拟机里访问到自己的本机里的本地项目
查看>>
浅谈对xmpp的理解及应用
查看>>
postgresql备份导入数据库小记
查看>>
翻译随笔
查看>>
java模拟post请求发送json,用两种方式实现,第一种是HttpURLConnection发送post请求,第二种是使用httpclient模拟post请求,...
查看>>
nfs挂载
查看>>
Dubbo坑
查看>>
Git
查看>>
Servlet生成验证码并在Servlet后台验证完成登陆
查看>>