PHP的Session运行机制
Session是一种用户与网站会话方式,一旦开启了Session会话,便可以在网站的任何页面使用(保持)这个会话。
# 会话流程
会话的工作流程很简单,当开始一个会话时,PHP 会尝试从请求中查找会话 ID (通常通过会话 cookie传递), 如果请求中不包含会话 ID 信息,PHP 就会创建一个新的会话。 会话开始之后,PHP 就会将会话中的数据设置到$_SESSION
变量中。 当 PHP 停止的时候,它会自动读取$_SESSION
中的内容,并将其进行序列化, 然后发送给会话保存管理器来进行保存。
# 会话保持
默认情况下,PHP 使用内置的文件会话保存管理器(files
)来完成会话的保存。 也可以通过配置项 session.save_handler
来修改所要采用的会话保存管理器。 对于文件会话保存管理器,会将会话数据保存到配置项 session.save_path
所指定的位置。
[Session]
; Handler used to store/retrieve data.
; http://php.net/session.save-handler
session.save_handler = files
; The file storage module creates files using mode 600 by default.
; You can change that by using
;
; session.save_path = "N;MODE;/path"
;
; where MODE is the octal representation of the mode. Note that this
; does not overwrite the process's umask.
; http://php.net/session.save-path
;session.save_path = "/tmp"
; Name of the session (used as cookie name).
; http://php.net/session.name
session.name = PHPSESSID
; Initialize session on request startup.
; http://php.net/session.auto-start
session.auto_start = 0
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
PHP脚本执行完毕之后,会话会自动关闭。 同时,也可以通过调用函数session_write_close()
来手动关闭会话。
# Session操作
# 创建session
可以通过调用函数session_start()
来手动开始一个会话,系统会分配一个会话 ID。 如果配置项session.auto_start设置为
1, 那么请求开始的时候,会话会自动开始。
session_start();
$_SESSION["username"] = "gitlib";
2
session_start()
是session机制的开始,它具有一定概率开启垃圾回收。这个概率是根据php.ini的session.gc_probability
配置项决定的,因为在有的系统中 session.gc_probability= 0
,即概率是0,这时就不具备垃圾回收。
; Defines the probability that the 'garbage collection' process is started
; on every session initialization. The probability is calculated by using
; gc_probability/gc_divisor. Where session.gc_probability is the numerator
; and gc_divisor is the denominator in the equation. Setting this value to 1
; when the session.gc_divisor value is 100 will give you approximately a 1% chance
; the gc will run on any given request.
; Default Value: 1
; Development Value: 1
; Production Value: 1
; http://php.net/session.gc-probability
session.gc_probability = 1
session.gc_divisor = 1000
; After this number of seconds, stored data will be seen as 'garbage' and
; cleaned up by the garbage collection process.
; http://php.net/session.gc-maxlifetime
session.gc_maxlifetime = 1440
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
概率是:session.gc_probability/session.gc_divisor
, 结果 1/1000。
# 读取session
PHP 内置的 $_SESSION 变量可以很方便的访问设置的 session 变量。
例子:
session_start();
echo "用户名:".$_SESSION["username"];
2
# 销毁session
可以通过session_unregister()
函数来注销单个session 变量或使用session_unset()
来注销整个 session 会话。
例子:
session_unset(); //注销 session 会话
也可以使用 unset() 函数来注销单个session 变量:
<?php
session_start();
unset($_SESSION["username"]);
?>
2
3
4
提示:不可使用 unset() 来销毁 session 会话。
# 自定义Session处理机制
默认配置下,Session的持久化是通过文件来存储,我们也可以将Session信息保存到Key-Value数据或Mysql数据库中,可以通过session_set_save_handler()
函数自定义session会话保存管理器。
语法:
# PHP5.4以前版本
session_set_save_handler ( callable $open , callable $close , callable $read , callable $write , callable $destroy , callable $gc [, callable $create_sid [, callable $validate_sid [, callable $update_timestamp ]]] ) : bool
# PHP5.4以后版本
session_set_save_handler ( object $sessionhandler [, bool $register_shutdown = TRUE ] ) : bool
2
3
4
5
session_set_save_handler() 设置用户自定义会话存储函数,如果想使用 PHP 内置的会话存储机制之外的方式, 可以使用本函数,例如,可以自定义会话存储函数来将会话数据存储到数据库。下面,以将会话保存的文件为例,说明一下这个函数的用法:
<?php
class FileSessionHandler
{
private $savePath;
// open 回调函数类似于类的构造函数, 在会话打开的时候会被调用。 这是自动开始会话或者通过调用 session_start() 手动开始会话 之后第一个被调用的回调函数。 此回调函数操作成功返回 TRUE,反之返回 FALSE。
function open($savePath, $sessionName)
{
$this->savePath = $savePath;
if (!is_dir($this->savePath)) {
mkdir($this->savePath, 0777);
}
return true;
}
// close 回调函数类似于类的析构函数。 在 write 回调函数调用之后调用。 当调用 session_write_close() 函数之后,也会调用 close 回调函数。
function close()
{
return true;
}
// 如果会话中有数据,read 回调函数必须返回将会话数据编码(序列化)后的字符串。 如果会话中没有数据,read 回调函数返回空字符串。
function read($id)
{
return (string)@file_get_contents("$this->savePath/sess_$id");
}
// 在会话保存数据时会调用 write 回调函数。 此回调函数接收当前会话 ID 以及 $_SESSION 中数据序列化之后的字符串作为参数。
function write($id, $data)
{
return file_put_contents("$this->savePath/sess_$id", $data) === false ? false : true;
}
// 当调用 session_destroy() 函数, 或者调用 session_regenerate_id() 函数并且设置 destroy 参数为 TRUE 时, 会调用此回调函数
function destroy($id)
{
$file = "$this->savePath/sess_$id";
if (file_exists($file)) {
unlink($file);
}
return true;
}
// 为了清理会话中的旧数据,PHP 会不时的调用垃圾收集回调函数。 调用周期由 session.gc_probability 和 session.gc_divisor 参数控制。 传入到此回调函数的 lifetime 参数由 session.gc_maxlifetime 设置。
function gc($maxlifetime)
{
foreach (glob("$this->savePath/sess_*") as $file) {
if (filemtime($file) + $maxlifetime < time() && file_exists($file)) {
unlink($file);
}
}
return true;
}
}
$handler = new FileSessionHandler();
session_set_save_handler(
array($handler, 'open'),
array($handler, 'close'),
array($handler, 'read'),
array($handler, 'write'),
array($handler, 'destroy'),
array($handler, 'gc')
);
// 下面这行代码可以防止使用对象作为会话保存管理器时可能引发的非预期行为
register_shutdown_function('session_write_close');
session_start();
// 现在可以使用 $_SESSION 保存以及获取数据了
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
在脚本执行完毕之后,PHP 内部会清除对象, 所以有可能不调用 write
和 close
回调函数。 这样可能会引发非预期的行为,所以当使用对象作为会话保存管理器时, 需要通过注册 shutdown 回调函数来规避风险。 通常,你可以通过调用**register_shutdown_function()**函数 来注册 'session_write_close'
回调函数。
# Session阻塞
默认配置下,PHP执行session_start(),就会取得文件独占锁,只有在该请求处理结束后,才会释放独占锁。这样,同时多个请求就会引起阻塞。
可以使用**session_write_close()**把数据写入session文件并结束session进程,这样就不需要等待页面执行完成,但这样有个问题,就是执行完sesssion_write_close()后,对session的任何写操作都不起作用,因为session进程已经结束,如果需要再写session时,在前面加上session_start()。
当然也可以通过session_set_save_handler自定义session处理过程,这里不做补充。