㈠ php中SESSION的问题
Session 和 Cookie 有什么关系
Cookie 也是由于 HTTP 无状态的特点而产生的技术。也被用于保存访问者的身份标示和一些数据。每次客户端发起 HTTP 请求时,会将 Cookie 数据加到 HTTP header 中,提交给服务端。这样服务端就可以根据 Cookie 的内容知道访问者的信息了。 可以说,Session 和 Cookie 做着相似的事情,只是 Session 是将数据保存在服务端,通过客户端提交来的 session_id 来获取对应的数据;而 Cookie 是将数据保存在客户端,每次发起请求时将数据提交给服务端的。
上面提到,session_id 可以通过 URL 或 cookie 来传递,由于 URL 的方式比 cookie 的方式更加不安全且使用不方便,所以一般是采用 cookie 来传递 session_id。
服务端生成 session_id,通过 HTTP 报文发送给客户端(比如浏览器),客户端收到后按指示创建保存着 session_id 的 cookie。cookie 是以 key/value 形式保存的,看上去大概就这个样子的:PHPSESSID=e4tqo2ajfbqqia9prm8t83b1f2。在 PHP 中,保存 session_id 的 cookie 名称默认叫作 PHPSESSID,这个名称可以通过 php.ini 中 session.name 来修改,也可以通过函数 session_name() 来修改。
为什么不推荐使用 PHP 自带的 files 型 Session 处理器
在 PHP 中,默认的 Session 处理器是 files,处理器可以用户自己实现(参见:自定义会话管理器)。我知道的成熟的 Session 处理器还有很多:Redis、Memcached、MongoDB……
为什么不推荐使用 PHP 自带的 files 类型处理器,PHP 官方手册中给出过这样一段 Note:
无论是通过调用函数 session_start() 手动开启会话, 还是使用配置项 session.auto_start 自动开启会话, 对于基于文件的会话数据保存(PHP 的默认行为)而言, 在会话开始的时候都会给会话数据文件加锁, 直到 PHP 脚本执行完毕或者显式调用 session_write_close() 来保存会话数据。 在此期间,其他脚本不可以访问同一个会话数据文件。
上述引用参见:Session 的基本用法
为了证明这段话,我们创建一下 2 个文件: 文件:session1.php
<?php
session_start();
sleep(5);
var_mp($_SESSION);
?>
文件:session2.php
<?php
session_start();
var_mp($_SESSION);
?>
在同一个浏览器中,先访问 http://127.0.0.1/session1.php,然后在当前浏览器新的标签页立刻访问 http://127.0.0.1/session2.php。实验发现,session1.php 等了 5 秒钟才有输出,而 session2.php 也等到了将近 5 秒才有输出。而单独访问 session2.php 是秒开的。在一个浏览器中访问 session1.php,然后立刻在另外一个浏览器中访问 session2.php。结果是 session1.php 等待 5 秒钟有输出,而 session2.php 是秒开的。
分析一下造成这个现象的原因:上面例子中,默认使用 Cookie 来传递 session_id,而且 Cookie 的作用域是相同。这样,在同一个浏览器中访问这 2 个地址,提交给服务器的 session_id 就是相同的(这样才能标记访问者,这是我们期望的效果)。当访问 session1.php 时,PHP 根据提交的 session_id,在服务器保存 Session 文件的路径(默认为 /tmp,通过 php.ini 中的 session.save_path 或者函数 session_save_path() 来修改)中找到了对应的 Session 文件,并对其加锁。如果不显式调用 session_write_close(),那么直到当前 PHP 脚本执行完毕才会释放文件锁。如果在脚本中有比较耗时的操作(比如例子中的 sleep(5)),那么另一个持有相同 session_id 的请求由于文件被锁,所以只能被迫等待,于是就发生了请求阻塞的情况。
既然如此,在使用完 Session 后,立刻显示调用 session_write_close() 是不是就解决问题了哩?比如上面例子中,在 sleep(5) 前面调用 session_write_close()。
确实,这样 session2.php 就不会被 session1.php 所阻塞。但是,显示调用了 session_write_close() 就意味着将数据写到文件中并结束当前会话。那么,在后面代码中要使用 Session 时,必须重新调用 session_start()。
例如:
<?php
session_start();
$_SESSION['name'] = 'Jing';
var_mp($_SESSION);
session_write_close();
sleep(5);
session_start();
$_SESSION['name'] = 'Mr.Jing';
var_mp($_SESSION);
?>
官方给出的方案:
对于大量使用 Ajax 或者并发请求的网站而言,这可能是一个严重的问题。 解决这个问题最简单的做法是如果修改了会话中的变量, 那么应该尽快调用 session_write_close() 来保存会话数据并释放文件锁。 还有一种选择就是使用支持并发操作的会话保存管理器来替代文件会话保存管理器。
我推荐的方式是使用 Redis 作为 Session 的处理器。
拓展阅读:
为什么不能用 memcached 存储 Session
如何使用 Redis 作为 PHP Session handler
Session 数据是什么时候被删除的
这是一道经常被面试官问起的问题。
先看看官方手册中的说明:
session.gc_maxlifetime 指定过了多少秒之后数据就会被视为"垃圾"并被清除。 垃圾搜集可能会在 session 启动的时候开始( 取决于 session.gc_probability 和 session.gc_divisor)。 session.gc_probability 与 session.gc_divisor 合起来用来管理 gc(garbage collection 垃圾回收)进程启动的概率。此概率用 gc_probability/gc_divisor 计算得来。例如 1/100 意味着在每个请求中有 1% 的概率启动 gc 进程。session.gc_probability 默认为 1,session.gc_divisor 默认为 100。
继续用我上面那个不太恰当的比方吧:如果我们把物品放在超市的储物箱中而不取走,过了很久(比如一个月),那么保安就要清理这些储物箱中的物品了。当然并不是超过期限了保安就一定会来清理,也许他懒,又或者他压根就没有想起来这件事情。
再看看两段手册的引用:
如果使用默认的基于文件的会话处理器,则文件系统必须保持跟踪访问时间(atime)。Windows FAT 文件系统不行,因此如果必须使用 FAT 文件系统或者其他不能跟踪 atime 的文件系统,那就不得不想别的办法来处理会话数据的垃圾回收。自 PHP 4.2.3 起用 mtime(修改时间)来代替了 atime。因此对于不能跟踪 atime 的文件系统也没问题了。
GC 的运行时机并不是精准的,带有一定的或然性,所以这个设置项并不能确保旧的会话数据被删除。某些会话存储处理模块不使用此设置项。
对于这种删除机制,我是存疑的。
比如 gc_probability/gc_divisor 设置得比较大,或者网站的请求量比较大,那么 GC 进程启动就会比较频繁。
还有,GC 进程启动后都需要遍历 Session 文件列表,对比文件的修改时间和服务端的当前时间,判断文件是否过期而决定是否删除文件。
这也是我觉得不应该使用 PHP 自带的 files 型 Session 处理器的原因。而 Redis 或 Memcached 天生就支持 key/value 过期机制的,用于作为会话处理器很合适。或者自己实现一个基于文件的处理器,当根据 session_id 获取对应的单个 Session 文件时判断文件是否过期。
为什么重启浏览器后 Session 数据就取不到了
session.cookie_lifetime 以秒数指定了发送到浏览器的 cookie 的生命周期。值为 0 表示"直到关闭浏览器"。默认为 0。
其实,并不是 Session 数据被删除(也有可能是,概率比较小,参见上一节)。只是关闭浏览器时,保存 session_id 的 Cookie 没有了。也就是你弄丢了打开超市储物箱的钥匙(session_id)。
同理,浏览器 Cookie 被手动清除或者其他软件清除也会造成这个结果。
为什么浏览器开着,我很久没有操作就被登出了
这个是称为“防呆”,为了保护用户账户安全的。
这个小节放进来,是因为这个功能的实现可能和 Session 的删除机制有关(之所以说是可能,是因为这个功能不一定要借住 Session 实现,用 Cookie 也同样可以实现)。 说简单一点,就是长时间没有操作,服务端的 Session 文件过期被删除了。
一个有意思的事情
在我试验的过程中,发现了小有意思的事情:我把 GC 启动的概率设置为 100%。如果只有一个访问者请求,该访问者即使过了很久(超过了过期时间)后才发起第二次请求,那么 Session 数据也还是存在的('session.save_path' 目录下面的 Session 文件存在)。是的,明明就超过了过期时间,却没有被 GC 删除。这时,我用另外一个浏览器访问时(相对于另一个访问者),这次请求生成了新的 Session 文件,而上一个浏览器请求生成的那个 Session 文件终于没有了(之前那个 Session 文件在 'session.save_path' 目录下面的消失了)。
还有,发现 Session 文件被删除后,再次请求,还是会生成和之前文件名相同的 Session 文件(因为浏览器并没有关闭,再次请求发送的 session_id 是相同的,所以重新生成的 Session 文件的文件名还是一样的)。但是,我不理解的是:这个重新出现的文件的创建时间竟然是第一次的那个创建时间,难道它是从回收站中回来的?(确实,我做这个试验时是在 window 下进行的)
我猜测的原因是这样:当启动会话后,PHP 根据 session_id 找到并打开了对应的 Session 文件,然后才启动 GC 进程。GC 进程就只检查除了当前这个 Session 文件外的其他文件,发现过期的就干掉。所有,即使当前这个 Session 文件已经过期了,GC 也没有删除它。
我认为这个不合理的。
由于发生这种情况影响也不大(毕竟线上请求很多,当前请求的过期文件被其他请求唤起的 GC 干掉的可能性是比较大的) + 我没有信心去看 PHP 源代码 + 我并不在线上使用 PHP 自带的 files 型 Session 处理器。所以,这个问题我就没有深入研究了。请谅解。
<?php
// 过期时间设置为 30 秒
ini_set('session.gc_maxlifetime', '30');
// GC 启动概率设置为 100%
ini_set('session.gc_probability', '100');
ini_set('session.gc_divisor', '100');
session_start();
$_SESSION['name'] = 'Jing';
var_mp($_SESSION);
?>
㈡ php 如何销毁SESSION中的指定值
unset();是删除变量
如果要删除$_SESSION['uid']
只需要unset($_SESSION['uid']);
如果要删除全部$_SESSION
只需要unset($_SESSION);
㈢ php中session_destroy()的作用
Session_destroy()销毁当前会话中的所有数据,但不重置与当前会话关联的全局变量,也不重置会话cookie。如果需要再次使用会话变量,则必须再次调用session_start()函数。
为了完全销毁会话,例如当用户注销时,必须同时重置会话ID。如果会话ID是由cookie传递的,那么还会调用setcookie()函数来删除客户机的会话cookie。
使用方法:
1.要在PHP中使用会话,首先需要启动会话。要启动会话,您需要使用PHP的内置函数session_start(),如图所示。
㈣ PHP中session变量的销毁
1.何为session?
相当于一个客户端(可以是浏览器、app、ftp等其他,而且同一个浏览器多开几个又算是不同的客户端)对服务器的一个访问,这个期间服务器为此建立一个唯一的标示(session_id
session_name),其实也就是一个数组Array(),Session的开始和结束并不以业务上的输入用户名密码开始,也不以关闭浏览器和网页刷新而结束
2.session变量的销毁
程序代码
<?php
session_unset();
session_destroy();
?>
session_unset()
释放当前在内存中已经创建的所有$_SESSION变量,但不删除session文件以及不释放对应的session
id
session_destroy()
删除当前用户对应的session文件以及释放session
id,内存中的$_SESSION变量内容依然保留
【注意】:
删除session方法:
1、unset
($_SESSION['xxx'])
删除单个session,unset($_SESSION['xxx'])
用来unregister一个已注册的session变量。其作用和session_unregister()相同。session_unregister()在PHP5中不再使用,可将之打入冷宫。
unset($_SESSION)
此函数千万不可使用,它会将全局变量$_SESSION销毁,而且还没有可行的办法将其恢复。用户也不再可以注册$_session变量。
2、$_SESSION=array()
删除多个session
3、session_destroy()结束当前的会话,并清空会话中的所有资源。。该函数不会unset(释放)和当前session相关的全局变量(globalvariables),也不会删除客户端的session
cookie.PHP默认的session是基于cookie的,如果要删除cookie的话,必须借助setcookie()函数。
返回值:布尔值。
功能说明:这个函数结束当前的session,此函数没有参数,且返回值均为true
session_unset()
如果使用了$_session,则该函数不再起作用。由于PHP5必定要使用$_session,所以此函数可以打入冷宫了。
可以得出删除session的步骤:
①session_start()
②$_SESSION=array()/unset($_session['xxx'])
③session_destroy()
㈤ php 如何删除单个session并释放内存
unset($_SESSION['users_user_id']);这个是注销一个session变量;
session_destroy();是注销所有的session变量,并且结束session会话;
也就是说用unset($_SESSION['users_user_id']);就行了
㈥ php为什么要销毁session
session_unset()
释放当前在内存中已经创建的所有$_SESSION变量,但不删除session文件以及不释放对应的session id
session_destroy()
删除当前用户对应的session文件以及释放session id,内存中的$_SESSION变量内容依然保留
因此,释放用户的session所有资源,需要顺序执行如下代码:
<?php
$_SESSION['user'] = 'lowell';
session_unset();
session_destroy();
?>也可以使用unset()销毁。一般在这几个语句都需要用户触发,比如用点 安全退出后,触发这几个销毁函数。自动销毁还需要左一个计划任务,来定时触发,或者页面监控触发,比如设个全局变量,当页面进来的时候,这个全局变量设置为true,每当退出的时候,检查这个全局变量是否存在并为true,如果不是就执行销毁函数。