PHP7语言基础——Cookie与Session
文章目录
- Cookie详解
- 基本概念和设置
- Cookie的应用和存储机制
- 删除Cookie
- Session详解
- 会话管理
- 创建会话
- PHP中的session工作机制
- PHP中的Session存储机制
- 注销和注销会话变量
- 扩展——使用Redis存储Session
HTTP协议是无状态协议,对于事物处理没有记忆能力。缺少状态意味着后续处理需要前面的信息,就必须重新传送数据,这样可能导致每次连接传送的数据量逐渐增大。为了弥补HTTP协议无状态的不足,两种用于保持HTTP连接状态的技术应运而生了,一个是Cookie,另一个是Session。其中Cookie存储在客户端,并显示永久的数据存储。Session将数据存储在服务器端,保证数据在程序的单词访问中持续有效。
Cookie详解 基本概念和设置 Cookie是独立语言而存在的,各种编程语言都有操作Cookie的能力。在实现过程中,编程语言通过指令告之浏览器,然后浏览器实现设置Cookie的功能。读取Cookie则是通过浏览器请求服务端时携带的HTTP头部中的Cookie信息的来的。
【PHP7语言基础——Cookie与Session】在PHP中可以使用
setcookie()
函数来设置cookie,其语法如下:setcookie(string $name[, string $value=""[, int $expire=0[, string $path=""[, string $domain=""[, bool $secure=false[, bool $httponly=false]]]]]]):bool
该函数定义了Cookie,会和剩下的HTTP头一起发送给客户端。和其他HTTP头一样,必须在脚本产生任意输出之前发送Cookie(由于协议限制)(包括
和
或者空格)之前调用该函数。一旦设置Cookie后,下次打开页面时可以使用$_COOKIE
读取。Cookie值同样也存在与$_REQUEST
中。如果在调用本函数以前就产生了输出,setcookie()
会调用失败并返回 FALSE
。 如果 setcookie()
成功运行,返回 TRUE
。参数:
- name:Cookie名称。
- value:Cookie值。这个值存储与用户的电脑中,因此不建议存储敏感信息。如
name
是cookiename
,可以通过$_COOKIE['cookiename']
获取它的值。 - expire:Cookie过期时间,这是个Unix时间戳,也就是说,基本可以用
time()
函数的结果加上希望过期的秒数。或者也可以用mktime()
来设置。如time()+60*60*24*30
表示Cookie30天后过期。如果设置为零,或者忽略该参数,Cookie会在会话结束时过期(也就是关闭浏览器时)。 - path:Cookie有效的服务器路径。设置成’/'时,Cookie对整个域名
domain
有效。如果设置成'/foo/'
,Cookie仅仅对domain
中/foo/
目录及其子目录有效。默认值是设置Cookie时的当前目录。 - domain:Cookie的有效域名/子域名。设置成子域名(如www.example.com),会使Cookie对这个子域名和它的三级域名有效(包括它的全部子域名),只要设置成域名就可以了。
- secure:设置这个Cookie是否仅仅通过安全的HTTPS连接传给客户端。设置成TRUE时,只有安全连接存在时才会设置Cookie。如果是在服务器端处理这个需求,程序员需要仅仅在安全连接上发送此类Cookie(通过
$_SERVER["HTTPS"]
判断)。 - httponly:设置成TRUE,Cookie仅可通过HTTP协议访问。就是说Cookie无法通过类似JavaScript这样的脚本语言访问。要有效减少XSS攻击时的身份窃取行为,建议用此设置(注意:这个选项不是所有浏览器都支持)。
程序运行结果:
文章图片
也许想看到的结果跟程序运行的结果不一样,我们来详细解释一下以上代码。首先第一个Cookie设置名为name、值为ib-top,其他参数都是默认值,表示在当前目录和域名下都有效,切有效时间持续到浏览器关闭,所以,
$_COOKIE
中输出了这个Cookie。第二个和第三个Cookie设置只在特定的目录域名和有效时间内才能看到。所以我们在$_COOKIE
中看不到相应信息。注意:第一次运行程序看不到任何Cookie信息,因为设置完Cookie后需要刷新页面,在下次HTTP请求时头部才会携带上一次设置的Cookie信息。
可以通过开发者工具箱来看一下当前请求的消息头(Request Headers)和响应消息头(Response Headers):
文章图片
可以看到返回消息头中包含了3个Set-Cookie部分,用于通知浏览器设置对应的Cookie。当我们再次刷新页面时,可以看到请求消息头中携带了Cookie信息。刷新请求得到的请求消息头如下:
文章图片
可见其中已经携带了Cookie信息,但是只有设置的name这一个Cookie,因为其他两个Cookie不在这目录或本域名下有效。
PHP和客户端JavaScript都可以操作Cookie,用PHP设置的Cookie也可以用JavaScript读取到,用JavaScript设置的Cookie也可以由PHP读取到。不同的是,PHP设置的Cookie需要在刷新页面后的下一次请求中才有效,而JavaScript设置的Cookie在本次请求中就有效。
【示例】
>
function setCookie(name, value) {
var Days = 30;
var exp = new Date();
exp.setTime(exp.getTime() + Days*24*60*60*1000);
document.cookie = name + "=" + escape(value) + ";
expires=" + exp.toGMTString();
}
function getCookie(name) {
var arr,reg=new RegExp("(^|)"+name+"=([^;
]*)(;
|$)");
if(arr=document.cookie.match(reg))
return unescape(arr[2]);
else
return null;
}
setCookie('test', 'testhahaha');
alert(getCookie('test'));
程序运行结果:
文章图片
访问以上通过JavaScript在客户端设置Cookie时的消息头如下:
文章图片
可以看到在第一次访问页面时向服务器端发送HTTP请求时就已经携带了Cookie信息。
Cookie的应用和存储机制 Cookie的工作原理:浏览器请求URL时,会首先扫描本地存储的Cookie,如果发现其中有何此URL相关联的Cookie,就会把它们返回给服务器端。
Cookie通常应用于以下几个方面:
- 在页面之间传递变量。因为浏览器不会保存当前页面上的任何变量信息,如果页面被关闭,那么页面上的所有变量信息也会消失。通过Cookie,可以把变量值在Cookie中保存下来,然后另外的页面可以重新读取这个值。
- 记录访客信息。利用Cookie可以记录客户曾经输入的信息或记录访问页面的次数。
- 把所查看的页面保存在Cookie临时文件夹中,可以提高以后的浏览速度。
- 用来存储一些不敏感信息,如用来防止刷票、记录用户名、限制重复提交等。
="text/javascript">
function setCookie(name, value) {
var Days = 30;
var exp = new Date();
exp.setTime(exp.getTime() + 60*100);
// 设置过期时间为1分钟
document.cookie = name + "=" + escape(value) + ";
expires=" + exp.toGMTString();
}
function getCookie(name) {
var arr,reg=new RegExp("(^|)"+name+"=([^;
]*)(;
|$)");
if(arr=document.cookie.match(reg))
return unescape(arr[2]);
else
return null;
}
function submit() {
if(getCookie('submit')){
alert('你已经提交过了,请一分钟之后再提交');
} else {
setCookie('submit','yes');
}
}
以上代码实现了防止用户在一分钟之内多次提交表单,当用户第一次提交表单时,设置Cookie有效期为1分钟,当再次提交时判断Cookie是否过期来限制用户的提交。
虽然,Cookie是存储在客户端的一段数据,但是不同的浏览器存储Cookie的地方不同:一种是将Cookie数据保存在文件中,另一种是保存在浏览器内存中。
在Windows系统上,IE浏览器Cookie数据位于
%APPDATA%\Microsoft\Windows\Cookies\
目录中的xxx.txt文件中。在IE浏览器中,IE将各个站点的Cookie分别保存为一个xxx.txt这样的纯文本文件;而Firefox和Chrome是将素有的Cookie都保存在一个文件中,该文件的格式为SQLite数据库格式的文件。Firefox的Cookie数据位于%APPDATA%\Mozilla\Firefox\Profiles\
目录中的xxx.default目录下,名为Cookies.sqlite的文件中。在Firefox中查看Cookie,可以选择“工具>选项>隐私>显示Cookie”。Chrome的Cookie数据位于
%LOCALAPPDATA%\Google\Chrome\User Data\Default\
目录中名为Cookies的文件中。删除Cookie
- 在浏览器中手动删除
由于Cookie自动生成的文本会存在与IE浏览器的Cookies临时文件夹中,在浏览器中删除Cookie文件是比较快捷的方法,具体步骤如下:
① 在浏览器页面中选择【工具】选项,在弹出的下拉菜单中选择【Internet选项】菜单。
② 打开【Internet选项】对话框,在【常规】选项卡中单击【删除】按钮。
③ 打开【删除浏览的历史记录】对话框,选中【Cookie】复选框,单击【删除】按钮即可。
- 使用函数删除
删除Cookie仍然使用setcookie()
函数。当删除Cookie时,将第二个参数设置为空,第三个参数的过期时间设置为小于系统的当前时间即可。
【示例】
在PHP中,每一个Session都有一个ID。这个Session ID是一个由PHP随机生成的加密数字。这个Session ID通过Cookie存储在客户端浏览器中,或者直接通过URL传递到客户端,如果在某个URL后面看到一长串加密的数字,这很有可能就是Session ID了。
使用Session ID打开服务器端相对应的session变量,跟用户相关的会话数据便一目了然。默认情况下,在服务器端的session变量数据是以文件的形式加以存储的,但是会话变量数据也经常通过数据库进行保存。
在浏览器中,有些用户处于安全性的考虑,关闭了其浏览器的Cookie功能。Cookie将不能正常工作。使用Session可以不需要手动设置Cookie,PHP Session可以自动处理。可以使用会话管理,及PHP中的
session_get_cookie_params()
函数来访问Cookie的内容。该函数返回一个数组,包括Cookie的生存周期、路径、域名、secure等。函数定义如下:session_get_cookie_params(void):array
函数返回一个如下形式的数组:
Array
(
[lifetime] => 0
[path] => /
[domain] =>
[secure] =>
[httponly] =>
[samesite] =>
)
其中各键代表的含义如下:
- lifetime:cookie的生命周期,以秒为单位。
- path:cookie的访问路径。
- domain:cookie的域。
- secure:近在使用安全连接时发送cookie。
- httponly:只能通过http协议访问cookie。
- samesite:该属性用来限制第三方cookie,从而减少安全风险,它有三个值:
- Strict:完全禁止第三方cookie,跨站点时,任何情况下都不会发送cookie,只有当前网页的 URL 与请求目标一致,才会带上 Cookie。
- Lax:规则稍稍放宽,大多数情况也是不发送第三方 Cookie,但是导航到目标网址的 Get 请求除外。
- None:无限制。
会话管理 一个完整的会话包括创建会话、注册会话变量、使用会话变量和删除会话变量等。
创建会话
创建session之前必须先开启Session,可使用
session_start()
开启Session,同Cookie一样,在开始前不能有任何输出内容,否则会出现如下警告:Warninig: session_start(): Cannot send session cookie - headers already sent
也可以修改php.ini中的
session.auto_start=0
为session.auto_start=1
,设置自动开启Session支持,这样就不必每次在使用Session的时候都加上session_start()
了。session_start()
函数首先检查当前是否已经存在一个会话,如果不存在,将创建一个全新的会话,并且这个会话可以访问超全局变量$_SESSION
数组。如果已经存在一个会话,函数会直接使用这个会话,加载已注册过的会话变量,然后使用。该函数的定义如下:session_start([array $options=array()]):bool
如果通过GET或者POST方式,或者使用cookie提交了会话ID,则会重现现有会话。
参数:
- options:该参数是衣蛾关联数组,如果提供,那么会用其中的项目覆盖会话配置指示中的配置项。次数组中的键无需包含session前缀。除了常规的会话配置指示项,还可以在此数组中包含
read_and_close
选项。如果将此选项的值设置为TRUE,那么会话文件会在读取完毕之后马上关闭,因此,可以在会话数据没有变动的时候,避免不必要的文件锁。
session_register()
函数创建session。在使用session_register()
函数之前,需要在php.ini文件中将register_globals
选项设置为on,session_register()
函数通过为会话登陆一个变量来隐含地启动会话。【示例1】
请求page1.php页面之后,第二个页面page2.php会包含会话数据。
page 2';
// 如果不是使用cookie方式传送会话ID,则使用URL改写的方式传送会话ID
echo '
. SID . '">page 2';
';
// 使用session变量
echo $_SESSION['favcolor'];
// green
echo $_SESSION['animal'];
// cat
echo date('Y m d H:i:s', $_SESSION['time']);
PHP中的session工作机制
当会话自动开始或者通过
session_start()
手动开始的时候,PHP内部会调用会话管理器的open
和read
回调函数。会话管理器可能是PHP默认的,也可能是扩展提供的(SQLite或者Memcached扩展),也可能是通过session_set_save_handler()
函数指定的用户自定义会话管理器。通过read
回调函数返回的现有会话数据(使用特殊的序列化格式存储),PHP会自动反序列化数据并填充$_SESSION
超全局变量。如果需要对会话进行命名,需在调用
session_start()
函数之前调用session_name
函数。如果在php.ini中启用了
session.use_trans_sid
选项,session_start()
函数会注册一个颞部输出管理器,该输出管理器完成URL重写的工作。如果用户联合使用了ob_start()
和ob_gzhandler
函数,那么函数的调用顺序会影响输出结果。例如,必须在会话开始之前先调用ob_gzhandler
函数完成注册。当在代码中设置了Session时,在HTTP请求的消息头中会携带一个名为PHPSESSID的Cookie,其值是一个32位十六进制的字符串。每个客户端向服务器请求时都会产生一个不同的值,如果清楚浏览器Cookie,再次刷新页面时将会重新设置一个PHPSESSID的值。服务端接收到这个Cookie,根据其值在服务器中找到对应的Session文件,从而实现保持与客户端链接状态的信息,其中Session中存储着序列化的Session键值等信息。设置了Session的HTTP请求消息头如下:
文章图片
PHP中的Session存储机制
默认情况下,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,那么将不会执行自动垃圾回收。注销和注销会话变量
注销会话变量使用
unset()
函数即可。如unset($_SESSION['name'])
。该函数用于释放指定的Session变量。如果要注销所有的会话变量,只需要给$_SESSION
赋值一个空数组就可以了。注销完成后,使用session_destroy()
销毁会话即可,其实就是清除相应的session ID.扩展——使用Redis存储Session 当我们的产品部署到生产环境后,访问量会越来越大,服务器的负载会越来越高,如果我们开启了session,那么就会生成很多的session文件,由于session默认是存储在硬盘上的,因此每次服务器去读取这些session文件都要经过许多I/O操作。PHP中可以使用
session_set_save_handle()
函数自定义session保存函数(如打开、关闭、写入、读取等)。该函数的定义如下: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
session_set_save_handler()
设置用户定义会话存储函数。如果想使用PHP内置的会话存储机制之外的方式,可以使用本函数。例如,可以自定义会话存储函数来将会话数据存储到数据库。参数:
- sessionhandler:实现了
SessionHandlerInterface
,SessionIdInterface
或SessionUpdateTimestampHandlerInterface
接口的对象,如:SessionHandler
。自PHP5.4之后可以使用。 - register_shutdown:将函数
session_write_close()
注册为register_shutdown_function()
函数。
-
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(stirng $sessionId, string $data)
在会话保存数据时会调用write回调函数。此回调函数接收当前会话ID以及$_SESSION
中数据序列化之后的字符串作为参数。序列化会话数据的过程由PHP根据session.serialize_handler
设定值来完成。
序列化后的数据将和会话ID关联在一起进行保存。当调用read回调函数获取数据时,所返回的数据必须要和传入wirte回调函数的数据完全保持一致。
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
参数控制。传入到此回调函数的lifttime
参数由session.gc_maxlifetiime
设置。此回调函数操作成功返回TRUE,失败返回FASLE。
-
create_sid()
当需要新的会话ID时被调用的回调函数。回调函数被调用时无传入参数,其返回值应该是一个字符串格式的、有效的会话ID。
我们使用
session_set_save_handler()
的第一种原型来实现自定义session存储。首先我们需要创建 一个用于session管理的类MySessionHandler,代码如下:
redis = new Redis();
$this->redis->connect('127.0.0.1', 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 = https://www.it610.com/article/$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的工作。在write回调函数中,以传入的session ID作为key,以session的值作为redis中key的值存入redis,并设置过期时间为60秒;read方法以传入的session ID为key从redis中取出相应的session值。destroy可根据传入的ID删除redis中的session。接下来我们编写另外一个设置session的脚本,将MySessionHandler引入,以便于使用我们的session管理类。
1, 2, 3, 4, 5);
最后我们编写一个读取session的脚本:
我们首先访问setmysession.php,然后访问getmysession.php,会在浏览器中得到如下输出:
文章图片
推荐阅读
- 急于表达——往往欲速则不达
- 慢慢的美丽
- 《真与假的困惑》???|《真与假的困惑》??? ——致良知是一种伟大的力量
- 2019-02-13——今天谈梦想()
- 考研英语阅读终极解决方案——阅读理解如何巧拿高分
- Ⅴ爱阅读,亲子互动——打卡第178天
- 低头思故乡——只是因为睡不着
- 取名——兰
- 每日一话(49)——一位清华教授在朋友圈给大学生的9条建议
- 广角叙述|广角叙述 展众生群像——试析鲁迅《示众》的展示艺术