如何在 PHP 中使用和管理 Cookie

HTTP 协议在设计之初,为了保持简单,本身是没有状态的,也就是说,对同一个客户端浏览器而言,上一次对服务器的请求和下一次请求之间是完全独立的、互不关联的,在服务器端并不能识别两次请求是同一个浏览器发起的,在不改变 HTTP 协议本身设计的前提下,为了解决这个问题,引入了 Cookie 技术来管理服务器与客户端之间的状态。

  • 会话状态管理(客户端记录 Session ID,用于管理用户登录状态、购物车或其它需要记录客户端状态的信息)
  • 个性化设置(如用户自定义设置、主题、本地化语言等)
  • 浏览器行为跟踪(如跟踪分析用户行为,用于站点访问信息统计、谷歌/百度等联盟广告的精准投放等)

我们可以在浏览器中通过控制台或者第三方插件很轻松的查看某个站点的所有 Cookie 信息,以 「学院君」网站首页为例,通过 Chrome 控制台的 Application->Storage->Cookies 导航可以看到 https://xueyuanjun.com 这个域名下的所有 Cookie:

-w1096

或者通过 Chrome 商店下载的管理站点 Cookie 的 EditThisCookie 插件查看当前站点的 Cookie 信息,使用这个插件的好处是可以对 Cookie 进行修改和设置:

-w602

需要注意的是,为了安全起见,Cookie 遵循浏览器同源策略,即不同站点(域名)之间不共享 Cookie,在一个站点下不能访问另一个站点的 Cookie,Cookie 必须和域名绑定,设置在指定域名下的 Cookie 只能在该域名下访问。

通过 setcookie 发送 Cookie

-w856

通过 setcookie 设置的 Cookie 会和已有的 Cookie 一起设置到 Set-Cookie 响应头和 HTTP 响应一起发送给客户端,如果请求头中已经包含同名 Cookie,则覆盖之。

setcookie 函数中,第一个参数 $name 是 Cookie 的名称,第二个参数 $value 是对应的 Cookie 值,接下来的几个参数是 Cookie 的属性:

<?php

setcookie('name', '学院君');
$expires = time() + 3600;
setcookie('website', 'https://xueyuanjun.com', $expires); // 1 小时后过期

echo '设置 Cookie 成功';

然后,我们在浏览器中访问 http://localhost:9000/cookie.php

-w1081

可以看到响应头中已经包含两个用于设置 Cookie 的 Set-Cookie 响应头,第二个 Cookie 还包含了过期信息( PHP 底层将过期信息转化为 expiresMax-Age 两个属性,前者表示具体过期时间点,后者表示剩余过期时间)。

在 EditThisCookie 扩展中也可以看到对应的站点 Cookie 信息了:

-w861

在 PHP 中,可以通过超全局变量 $_COOKIE 来获取请求中的 Cookie 信息,通过访问关联数组的方式访问指定名称的 Cookie 值即可:

$name = $_COOKIE['name'];
$website = $_COOKIE['website'];

但是需要注意的是,本次响应发送的 Cookie 需要在下次请求时才能在服务端获取到,这很好理解,因为 Cookie 是随着响应头发送到客户端,再由客户端下次请求时自动在请求头中带上 Cookie 信息对服务器发起请求,服务器通过解析请求头才能获取到上次发送给客户端的 Cookie。

我们改写上述 cookie.php 的实现,新增读取 Cookie 的逻辑:

<?php

if (isset($_GET['action']) && $_GET['action'] == 'get_cookies') {
    $name = $_COOKIE['name'];
    $website = $_COOKIE['website'];
    printf('从用户请求中读取的 Cookie 数据:{name: %s, website: %s}', $name, $website);
    exit();
}
setcookie('name', '学院君');
$expires = time() + 3600;
setcookie('website', 'https://xueyuanjun.com', $expires); // 1 小时后过期

header('Location: /cookie.php?action=get_cookies');

-w911

我们更新了 name 对应的 Cookie 有效期为 1 天,访问 http://localhost:9000/cookie.php?action=set_cookies,可以看到过期时间已经调整:

-w845

我们可以通过 setcookie 函数设置要删除的 Cookie 过期时间为过去的时间,这样响应发送到客户端后,客户端判定该 Cookie 已过期,然后主动将其删除:

在浏览器中访问 http://localhost:9000/cookie.php?action=del_cookies,可以看到响应头中 Set-Cookie 已经将 website 标识为已删除,过期时间也是 Unix 元年(过去的时间):

-w1013

通过 EditThisCookie 扩展也可以看到 Cookie 列表中 website 已经不复存在了:

-w857

当然,在服务端通过 $_COOKIE['website'] 也无法访问到它了,不仅如此,还会抛出一个 Notice 级别的错误,因为对应的关联数组索引不存在:

-w873

小结

通过上面的介绍,想必你已经对 Cookie 的基本原理和增删改查实现有了大致的了解,首先 Cookie 需要在服务端生成,然后通过 Set-Cookie 响应头发送给客户端浏览器,客户端浏览器会将服务器发送过来的 Cookie 数据存储在本地,并根据过期时间对其进行维护,已过期的 Cookie 会自动删除,未设置过期时间的 Cookie 生命周期随着浏览器关闭而终结(这种 Cookie 也可以称之为 Session Cookie),然后下次客户端向服务端发起请求时,就会带上对应域名作用域下的所有 Cookie,服务端根据这些 Cookie 可以感知客户端信息,从而实现 HTTP 状态管理,实际上 Session 技术正是基于存储在 Cookie 中的 Session ID 实现对用户登录状态的管理的,所不同的是,Session 数据是存储在服务端的,然后通过客户端 Session ID 识别并维护用户认证状态。下篇教程,我们来探讨 Session 技术的实现原理和增删改查的实现。

上一篇: PHP 发送 HTTP 响应与文件下载

下一篇: 在 PHP 中使用和管理 Session 并实现简单的用户登录功能