资讯专栏INFORMATION COLUMN

详解Session

joy968 / 1462人阅读

摘要:会在脚本执行完毕或调用函数之后调用此回调函数。此回调函数操作成功返回,反之返回。在回调函数中,以传入的作为,以的值作为中的值存入,并设置过期时间为秒方法以传入的为从取出相应的的值。

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实践指南》 陈小龙著

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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/22538.html

相关文章

  • Cookie 和 session详解

    摘要:协议是无状态的,一旦数据交换完毕,客户端与服务器端的连接就会关闭,再次交换建立新的连接,也就是说,服务器无法跟踪会话。而和就是用与解决这种问题。值得一提的是是建立在的基础上创建的。注意在的文件中,设置了的生命周期最长为分钟。 在将cookie 和 session 之前需要先理解什么是会话会话: 用户打开一个浏览器,点击多个超链接,访问多个web资源,然后关闭浏览器,整个过程称为一个...

    wow_worktile 评论0 收藏0
  • Cookie和Session详解

    摘要:例如要想在多个二级域名中共享,需要设置为顶级域名,这样就可以在所有二级域名里面或者到这个的值了。顶级域名只能获取到设置为顶级域名的,设置为其他子级域名的无法获取。 Cookie和Session详解 Cookie Cookie只存储在客服端 Cookie是什么:Cookies是web服务器存放在用户硬盘的一段文本,Cookies允许一个wen站点在用户的机器存放一些文本的信息,并可以在以...

    Little_XM 评论0 收藏0
  • 详解 Cookie 和 Session 关系和区别

    摘要:目前大多数的应用都是用实现跟踪的。的安全性一般,他人可通过分析存放在本地的并进行欺骗。在安全性第一的前提下,选择更优。考虑到减轻服务器性能方面,应当适时使用。因此,维持一个会话的核心就是客户端的唯一标识,即。 showImg(https://segmentfault.com/img/bV8riL?w=800&h=444); 在技术面试中,经常被问到说说Cookie和Session的区别...

    microelec 评论0 收藏0
  • Laravel Dependency Injection (依赖注入) 概念详解

    摘要:依赖注入并不限于构造函数作为经验,注入最适合必须的依赖关系,比如示例中的情况注入最适合可选依赖关系,比如缓存一个对象实例。 本文翻译自 Symfony 作者 Fabien Potencier 的 《Dependency Injection in general and the implementation of a Dependency Injection Container in P...

    Fundebug 评论0 收藏0
  • cookie与session的使用(详解

    摘要:什么是用来存储客户端的一小段文本是一门客户端的技术因为是存储在客户端浏览器中的是为了实现客户端与服务器端之间的状态的保持技术,不安全,不要使用存储敏感信息比如登录状态和登录信息一些敏感的数据应该存储在服务器端的值从哪里来的当你访问一个网站这 什么是cookie, 用来存储客户端的一小段文本是一门客户端的技术 因为cookie是存储在客户端浏览器中的是为了实现 客户端与服务器端之间的状态...

    liujs 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<