初识PHP会话

1,211 阅读7分钟

前言

最近在学习Laravel 这套框架,发现对PHP的会话不是很了解,于是乎看了PHP官网手册,也看了慕课网的SESSION视频教程,看完后总觉得缺点什么,咦~,缺点在掘金上写篇读书笔记,唠叨唠叨,也可以检验一下自己学习的怎么样,废话不多说,直接切入正题

什么是会话?

在了解PHP会话技术前,我们先讨论一件事情,那就是什么是会话。从字面意义上来说,应该是一次会谈,一次交流,没错就是一次谈话,如果是谈话的话就需要有两个谈话对象,对计算机世界而言,谈话对象就是客户端和服务器端,客户端说一句话,服务器回答一句。

上面的话说得太直白了,如果用计算机专业术语来解释会话,就是这样子的:

会话是浏览器和服务器之间的多次请求和响应

好像似懂非懂的样子,解释一下的话,就是客户端访问服务器,到服务器做出响应,最后客户端关闭退出,在这段时间里产生的请求和响应,称为客户端和服务器的一次会话

会话解决了什么问题呢?

会话的出现,对程序员来说又有一个知识点要考了(哈哈),可不可以不用会话呢?答案是不可以的,除非你能证明你是自己。这就和现实世界是一样的,你能证明你自己嘛,不能,你要拿出你的身份证才可以证明你自己。

在计算时间里是怎么解决你是你自己的这个世界难题的呢?使用HTTP协议,好像不行,HTTP协议是无状态的,它是没得指望的了。怎么办呢,计算机大佬们设计了一套会话技术,专门来处理“记住我”的问题

PHP会话工作原理

如果要理解起来还是很麻烦的,用一张图来解释是最合适不过的

我模拟一下图中操作,就可以大致的了解PHP会话的工作原理了

我们第一次访问PHP官网,浏览器发出一个HTTP请求到服务器,服务器接收到客户端的请求。服务器发现还没有和客户端建立会话,进行session初始化操作,创建session_id,session文件等操作

执行代码,如果在执行的过程中有session数据产生,则将数据存放在超全局数组$_SESSION中,代码执行完毕,把$_SESSION中的数据序列化后存放在session文件中,并设置一个会话cookie,cookie名为session_name(),cookie值为session_id(),把cookie信息携带在对客户端的响应中,一并返回给客户端

第二次你访问了PHP官网,还是会执行session初始化,发现你发出的HTTP请求携带有session_id,同时去读取这个session_id对应的session文件,并把文件中的数据读取到$_SESSION数组中,继续执行代码,如果有设置session数据,把数据暂存在$_SESSION数组中,最后代码执行完毕,把数组中的数据序列化后存放在session文件里,同样的把sesion_id通过cookie方式存放在客户端 ,下次在访问网站的时候,发送给服务器端就是了

网站访问完了退出登录,这个时候服务器会销毁session,把客户端的cookie设置过期,并删除服务器端的session文件

如果下次还访问PHP官网,重复以上操作

几个问题?

通过上面的介绍,我发现了这样一个问题,为什么服务器在第一次已经给客户端发送了cookie后,在第二次、第三次...都发送cookie呢,而且内容还是一样的,这会导致网络开销的啊。

确实如此,每次发送也不好,为什么这么做呢?

我大致猜一下这其中的原因,首先会话是有时间限制的,有的浏览器关闭退出后结束会话,有的会话是两个小时内的活跃用户

每次重置cookie,使客户端的cookie一直处于活跃状态,这样浏览器就不会清除cookie了

如果不更新的话,cookie一旦过期,用户就不能使用这个cookie,这样将导致用户必须重新登录网站后才可以继续使用。

通过每次对用户的响应携带着cookie信息,重置cookie的过期时间,让客户端的cookie处于活跃状态。

第二种情况就是,通过每次发送cookie到客户端,重置客户端的cookie,防止客户端的cookie被篡改,这种做法是很有限的,并不能完全的杜绝

以上就是我想到的为什么要每次都给客户端发送cookie的原因,我也没有去验证这两种说法,后面我会去找找看是什么原因

为什么你一直在说cookie?

这篇文章不是在说session的吗,怎么上面一直在提cookie,cookie又是什么东西。cookie简单的来说就是实现session的一种方式,session的实现也可以使用URL来实现,只不过cookie要好一些,这里暂时不展开去说cookie,我会用另一篇文章去做介绍

使用PHP实现会话

php中已经为我们实现了很多处理session的方法,通过这些方法就可以去实现session的初始化、设置session数据、session销毁等等操作

这里我列出了常用的session方法,不知道如何使用的,可以随时在php官网进行查找,还有其他方法,你可以通过链接进行访问

创建会话

在PHP中使用session_start()就可以开始一个会话了,当然如果请求中的cookie携带了会话ID,会重用这个会话,不会创建新的会话

在PHP具体的工作会做什么工作呢,通过PHP手册我们可以得到这些:

当会话开始的时候,PHP内部会使用会话管理器的open和read回调函数,这个会话管理器可以是PHP自带的,也可以是你自己定义的(后面我会实现一个自己的会话管理器),通过session_set_save_handler来设置自定义的会话管理器,使用read回调函数返回的会话数据,通过反序列化数据并把数据填充到$_SESSION全局变量中

在开启会话之前我们可以调用session_name()对会话命名,使用session_id()获得sessionID

会话管理器

我们在开启一个会话的时候,PHP会使用会话管理器来对会话数据进行处理,默认的PHP会话管理器是files,这将导致一个问题,它会锁定会话文件,对并发请求很大的网站来讲,这将是致命的。一个比较但简单的做法是,每次修改完会话数据后,使用session_write_close()来保存会话并释放文件锁,这种做法是非常不建议的。

推荐使用可以并发操作的会话管理器来代替文件保存管理器,首选当然是推荐Redis作为session的会话管理操作,为什么不使用memcached存储session呢?网上的说法是memcaced是缓存数据而不是存储数据的,memcached的LAR算法针对每个slab类执行,而不是针对整体,这样导致session最老的用户会掉线,具体的原因这里就不在深究,我也没有用过memcached作为session管理

传送会话ID

在第二次访问网站的时候,会把sessionID发送给服务器,发送sessionID方式有两种:

  • URL参数
  • Cookies

极力推荐使用Cookie的方式来传递会话ID,如果用户禁用Cookie,友情提示用户开启,Cookie相关的设置选项我会在会话安全中列出来

销毁会话

用户登出的时候要清除会话,销毁会话操作比起创建来说是比较麻烦的,具体分为三部分,

第一步重置会话变量

php中的会话数据存放在全局变量$_SESSION中,需要先把全局变量重置,重置的方法如下:

$_SESSION = array();

不要使用unset($_SESSION)来复位超级变量,因为这样导致无法继续使用$_SESSION来注册会话变量

第二步删除会话cookie

为了彻底的销毁会话,需要把客户端的会话cookie删除掉

$params= session_get_cookie_params();
setcookie(session_name(),'',time()-1,$params['path'],$params['domain'],$params['secure'],$params['httponly']);

让cookie过期就可以,这样浏览器会自动删除过期的cookie

如果开启了session.use_strict_mode配置项后,可以不用手动删除cookie,因为会话模块不会接受已经过期会话ID的cookie了,它会生产一个新的会话ID cookie,建议开启这个配置项

第三步销毁会话数据

最后使用函数session_destroy来删除会话数据

session_destroy();

如果你使用的是file作为会话管理器,那么还会删除session文件

什么时候删除会话数据?

php默认会使用gc(garbage collection 垃圾回收)管理会话数据,什么时候启动gc进程呢?

首先通过session.gc_maxlifetime 指定了过多少秒后的数据视为垃圾会话并进行清除

垃圾收集器会在会话开启的时候,根据session.gc_probability与 session.gc_divisor 的设置来启动垃圾收集器gc。 比如,session.gc_probability 为 1,session.gc_divisor为 100,那么就有1/100的概率来启动垃圾收集器gc

如果gc_probability/gc_divisor设置的过大,会频繁的启动gc来管理会话数据;设置的太小会有很多的过期会话数据没有即时清除,浪费存储空间

gc进程处理过期会话数据也是比较粗糙的,它会遍历所有的session文件,查看文件修改时间和当前系统时间,来判断文件是否过期而删除文件

所以php自带的file会话管理器工作效率是很低的,同时也不支持并发访问会话文件,而 Redis 或 Memcached 天生就支持 key/value 过期机制的,用于作为会话处理器非常合适

结束

至此php中的会话基本操作就是这些了,但对会话如何使用,如何保证会话是安全的,这也是非常重要的,防人之心不可无啊。

后面我会用两个章节来学习会话安全,以及Laravel中的session