Session一般被称为“会话控制”。比如打电话时从拿起电话拨号到挂断电话这中间的一系列过程可以称之为一个session。
具体到web中的session会话指的就是用户在浏览某个网站时,从进入网站到网站关闭所经过的这段时间,也就是用户浏览这个网站所花费的时间。因此从上述描述中可以看到,session是一个特定的时间概念。
为什么要使用session
首先我们要知道HTTP是一种无状态的协议,即同一个客户端的本次请求和上次请求没有任何关系,HTTP服务器并不知道这两个请求来自同一个客户端,这样设计的优点在于减轻服务器压力,提高速度。但相应的我们会面临这样一个问题:比如我在www.xuxiaoke.com/login.php里面登陆了,我在www.xuxiaoke.com/index.php 也希望是登陆状态,但是这是2个不同的页面,也就是2个不同的HTTP请求,这2个HTTP请求是无状态的,也就是无关联的,所以无法单纯的在index.php中读取到它在login.php中已经登陆了。那怎么办呢?总不能每个页面都登录一次吧。
正是这种诉求,一个新的客户端存储数据方式出现了:cookie。cookie是把少量的信息存储在用户自己的电脑上,它在一个域名下是一个全局的,只要设置它的存储路径在域名www.xuxiaoke.com下 ,那么当用户用浏览器访问时,php就可以从这个域名的任意页面读取cookie中的信息。所以就很好的解决了我在www.xuxiaoke.com/login.php页面登陆了,我也可以在www.xuxiaoke.com/index.php获取到这个登陆信息了。
虽然这种方案很不错,也很快速方便,但是由于cookie 是存在于用户端,而且它本身存储的尺寸大小也有限,最关键是用户是可以看见的,并可以随意的修改,很不安全。那如何又要安全,又可以方便的全局读取信息呢?于是,这个时候,一种新的存储会话机制:session 诞生了。
session 诞生了,从上面的描述来讲,它就是在一次会话中解决2次HTTP的请求的关联,让它们产生联系,让2两个页面都能读取到找个这个全局的session信息。session信息存在于服务器端,所以也就很好的解决了安全问题。
session的运行机制
session既然是一种服务区存储数据的方式,肯定存在于服务器的某个文件夹内。确实,它存在服务器的/tmp 目录下。
当我们启用session时,第一步是开启session,利用session_start()函数开启session。
session_start()是个无任何返回值的函数,既不会报错,也不会成功。它的作用是开启session,并随机生成一个唯一的32位的session_id。
session的全部机制也是基于这个session_id,它用来区分哪几次请求是一个人发出的。为什么要这样呢?因为HTTP是无状态无关联的,一个页面可能会被成百上千人访问,而且每个人的用户名是不一样的,那么服务器如何区分这次是小王访问的,那次是小明访问的呢?所以就指定唯一的session_id 来绑定一个用户。一个用户在一次会话上就是一个session_id,这样成千上万的人访问,服务器也能区分到底是谁在访问了。
下面我们来做个试验,在本地测试环境的根目录建立一个session.php,并输入以下代码
session_start();
echo "SID: ".SID."
";
echo "session_id(): ".session_id()."
";
echo "COOKIE: ".$_COOKIE['PHPSESSID'];
?>
SID 是一个系统常量,SID包含着会话名以及会话 ID 的常量,格式为 “name=ID”,
或者如果会话 ID 已经在适cookie 中设定时则为空字符串,第一次显示的时候输出的是SID的值,
当你刷新的时候,因为已经在cookie中存在,所以显示的是一个空字符串。
session_id() 函数用来返回当前会话的session_id,它会去读取cookie中的name,也就是PHPSESSID值
当我们访问这个页面时会出现以下内容
此时我们发现出现了一个警告,当我们再次刷新的时候,警告消失了
这里$_COOKIE[‘PHPSESSID’]中有值了,而且之后不论如何刷新,session_id 都是一样。这里面PHPSESSID的过期时间是会话,什么意思呢?就是浏览器只要不关就一直不存,浏览器一关就过期消失了。接下来我们关掉浏览器,重新打开session.php页面,看看有没有什么变化,我们会发现又回到当初第一次打开时候的样子。
原因就是每次我们访问一个页面,如果有开启session,也就是有session_start() 时,就会自动生成一个session_id 来标注是这次会话的唯一ID,同时也会自动往cookie里写入一个名字为PHPSESSID的变量,它的值正是session_id,当这次会话没结束,再次访问的时候,服务器会去读取这个PHPSESSID的cookie是否有值有没过期,如果能够读取到,则继续用这个session_id,如果没有,就会新生成一个session_id,同时生成PHPSESSID这个cookie。由于默认生成的这个PHPSESSID cookie是会话,也就是说关闭浏览器就会过期掉,所以,下次重新浏览时,会重新生成一个session_id。
session_id就是用来标识绑定一个用户的,既然session_id生成了。那么我们往session里面写入的数据是如何保存的,答案是保存在服务器的临时目录里,因为我是搭建的本地服务器环境,我电脑上的这个session是存在D:\phpstudy\PHPTutorial\tmp \tmp目录里的。
那么它的保存形式是什么样的?这时会用到session_id,session_id是32位的。服务器会用 sess_前缀 + session_id 的形式存在这个临时目录下,比如下面这个例子:
所以,每一次生成的session_id都会生成一个这样的文件,用来保存这次会话的session信息。
下面我们往session里写入些数据,来看看session是怎么往这个文件写数据的,我们在session.php页面继续加上写入session的语句:
$_SESSION['xuxiaoke'] = 'xiaoake';
$_SESSION['huanghuang'] = 'ahuang';
我们用编辑器打开它的存储文件sess_cmsggb6t4jakiu7om5r0m1s8e7这个文件,看看里面是啥?
是序列化的数据,我们肉眼也能读出来。当我们往$_SESSION全局变量里写数据时,它会自动往这个文件里写入。读取session的时候,也会根据session_id 找到这个文件,然后读取需要的session变量。
这个sess文件不会随着客户端的PHPSESSID过期,也一起过期掉,它会一直存在,除非GC扫描到它过期或者使用session_destroy()函数摧毁。
总结
HTTP请求一个页面后,如果用到开启session,会去读cookie中的PHPSESSID是否有,如果没有,则会新生成一个session_id,先存入cookie中的PHPSESSID中,再生成一个sess_前缀文件。当有写入$_SESSION的时候,就会往sess_文件里序列化写入数据。当读取的session变量的时候,先会读取cookie中的PHPSESSID,获得session_id,然后再去找这个sess_sessionid文件,来获取对应的数据。由于默认的PHPSESSID是临时的会话,在浏览器关闭后,会消失,所以,我们重新访问的时候,会新生成session_id和sess_这个文件。
session相关的函数
session_start — 启动新会话或者重用现有会话
bool session_start ([ array $options = array() ] )
$options此参数是一个关联数组,如果提供,那么会用其中的项目覆盖 会话配置指示 中的配置项。此数组中的键无需包含 session. 前缀。
除了常规的会话配置指示项, 还可以在此数组中包含 read_and_close 选项。如果将此选项的值设置为 TRUE, 那么会话文件会在读取完毕之后马上关闭, 因此,可以在会话数据没有变动的时候,避免不必要的文件锁。
session_start()会创建新会话或者重用现有会话。 如果通过 GET 或者 POST 方式,或者使用 cookie 提交了会话 ID, 则会重用现有会话。
session_id — 获取/设置当前会话 ID
string session_id ([ string $id ] )
如果指定了 id 参数的值, 则使用指定值作为会话 ID。 必须在调用 session_start() 函数之前调用 session_id() 函数。 不同的会话管理器对于会话 ID 中可以使用的字符有不同的限制。 例如文件会话管理器仅允许会话 ID 中使用以下字符:a-z A-Z 0-9 , (逗号)和 – (减号)
session_name — 读取/设置会话名称
string session_name ([ string $name ] )
session_name() 函数返回当前会话名称。 如果指定 name 参数, session_name() 函数会更新会话名称, 并返回原来的会话名称。需要在调用 session_start() 函数之前调用 session_name() 函数。
session_destroy — 销毁一个会话中的全部数据
bool session_destroy ( void )
session_destroy() 销毁当前会话中的全部数据, 但是不会重置当前会话所关联的全局变量, 也不会重置会话 cookie。 如果需要再次使用会话变量, 必须重新调用 session_start() 函数。
session相关的配置选项
session.auto_start(boolean)
session.auto_start指定会话模块是否在请求开始时自动启动。默认为0(不启动)
session.name(string)
指定会话名以用作cookie的名字,只能由数字字母构成,默认为PHPSESSID
session.save_handler(string)
定义用来存储和获取与会话关联的数据的处理器的名字,默认为files
session.save_path(string)
定义传递给存储处理器的参数,如果选择默认的files文件处理器,则值时文件的路径
session.gc_maxlifetime(integer)
指定过了多少秒之后数据就会被视为垃圾并被清除
session.gc_probability(integer)、session.gc_divisor(integer)
定义在每个会话初始化时启动gc(garbage collection,垃圾回收)进程的概率。此概率通过gc_probability/gc_divisor计算。