详解Session

1 Session的基本概念和设置php

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

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

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

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

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.8shell

Accept-Encoding:gzip, deflate, sdch, br数据库

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

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,代码以下:

<?php

class sessionmanager{

private $redis;

private $sessionsavepath;

private $sessionname;

public function __construct()

{

$this->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类。代码以下:

<?php

include 'sessionmanager.php';

new sessionmanager();

$_SESSION['namehaha'] = 'lixiaolong';

$_SESSION['namehah'] = 'lixiaolong';

$_SESSION['namehaa'] = 'lixiaolong';

$_SESSION['namhaha'] = 'lixiaolong';

$_SESSION['namhaha'] = array(‘a'=>1,2,3,4,4);

?>

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

<?php

include 'sessionmanager.php';

new sessionmanager();

var_dump($_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

相关文章
相关标签/搜索