Cookie 基础
概述
Cookie 是服务器发送到客户端浏览器并存储的一小段数据,浏览器会在后续请求中自动携带。PHP 通过 setcookie() 函数设置 Cookie,通过 $_COOKIE 超全局数组读取。正确配置 Cookie 的安全属性(HttpOnly、Secure、SameSite)对 Web 安全至关重要。
适用场景
- 用户登录状态
- 用户偏好设置
- 购物车数据
- 追踪分析
基础概念
setcookie() 参数
php
<?php
setcookie(
string $name, // Cookie 名称
string $value = "", // Cookie 值
int $expires_or_options = 0, // 过期时间戳或选项数组
string $path = "", // 有效路径
string $domain = "", // 有效域名
bool $secure = false, // 仅 HTTPS
bool $httponly = true // 仅 HTTP 访问(禁止 JS)
);PHP 7.3+ 数组选项语法
php
<?php
// PHP 7.3+ 推荐使用数组选项
setcookie('session_id', $sessionId, [
'expires' => time() + 3600,
'path' => '/',
'domain' => 'example.com',
'secure' => true,
'httponly' => true,
'samesite' => 'Lax', // PHP 7.3+
]);Cookie 安全属性
| 属性 | 说明 | 推荐值 |
|---|---|---|
HttpOnly | 禁止 JavaScript 访问 | true |
Secure | 仅通过 HTTPS 传输 | true |
SameSite | 跨站请求策略 | Lax 或 Strict |
Path | 有效路径范围 | / 或具体路径 |
Domain | 有效域名 | 具体域名 |
Expires | 过期时间 | 按需设置 |
PHP 8.0+ 变更
setcookie() 和 setrawcookie() 的 $expires 参数类型更严格。PHP 8.0 中如果过期时间不是整数类型,会触发 TypeError。
语法与代码示例
设置和读取 Cookie
php
<?php
// 设置 Cookie
setcookie('username', 'Alice', [
'expires' => time() + 86400, // 24 小时后过期
'path' => '/',
'secure' => true,
'httponly' => true,
'samesite' => 'Lax',
]);
// 设置多个 Cookie
setcookie('theme', 'dark', time() + 86400 * 30, '/', '', true, true);
setcookie('lang', 'zh-CN', time() + 86400 * 30, '/', '', true, true);
// 读取 Cookie
$username = $_COOKIE['username'] ?? 'Guest';
$theme = $_COOKIE['theme'] ?? 'light';
echo "用户: {$username}, 主题: {$theme}\n";
// 删除 Cookie(设置过期时间为过去)
setcookie('username', '', [
'expires' => time() - 3600,
'path' => '/',
]);Cookie 安全配置
php
<?php
// 安全的 Cookie 设置函数
function setSecureCookie(string $name, string $value, int $ttl = 3600): void
{
setcookie($name, $value, [
'expires' => time() + $ttl,
'path' => '/',
'domain' => $_SERVER['HTTP_HOST'],
'secure' => !empty($_SERVER['HTTPS']),
'httponly' => true,
'samesite' => 'Lax',
]);
}
// 使用
setSecureCookie('user_id', '12345', 7200);$PHPSESSID Cookie
php
<?php
// 当使用 session 时,PHP 会自动设置 $PHPSESSID Cookie
session_start();
echo "Session ID: " . session_id() . PHP_EOL;
// 可以通过 php.ini 配置 $PHPSESSID 的属性
/*
session.cookie_httponly = 1 ; HttpOnly
session.cookie_secure = 1 ; Secure(HTTPS only)
session.cookie_samesite = Lax ; SameSite
session.cookie_path = / ; 路径
session.cookie_domain = ; 域名
session.cookie_lifetime = 1440 ; 过期时间(秒)
session.name = PHPSESSID ; Cookie 名称
session.use_strict_mode = 1 ; 严格模式(PHP 7.1+)
*/
// 检查 Session Cookie 属性
$headers = headers_list();
foreach ($headers as $header) {
if (str_contains(strtolower($header), 'set-cookie')) {
echo $header . PHP_EOL;
}
}SameSite 属性
php
<?php
// SameSite 属性值
// Strict:完全禁止跨站携带 Cookie
setcookie('token', $value, [
'expires' => time() + 3600,
'samesite' => 'Strict',
]);
// Lax:允许安全的跨站请求(导航跳转)携带 Cookie
// 默认值(PHP 7.3+)
setcookie('session_id', $sid, [
'expires' => time() + 3600,
'samesite' => 'Lax',
]);
// None:允许所有跨站请求携带(必须配合 Secure)
setcookie('tracking_id', $tid, [
'expires' => time() + 86400 * 365,
'secure' => true,
'samesite' => 'None',
]);实战示例
Cookie 管理类
php
<?php
declare(strict_types=1);
class CookieManager
{
private bool $secure;
private string $domain;
private string $path;
public function __construct(bool $secure = true, string $domain = '', string $path = '/')
{
$this->secure = $secure;
$this->domain = $domain;
$this->path = $path;
}
public function set(string $name, string $value, int $ttl = 3600, string $samesite = 'Lax'): void
{
setcookie($name, $value, [
'expires' => time() + $ttl,
'path' => $this->path,
'domain' => $this->domain,
'secure' => $this->secure,
'httponly' => true,
'samesite' => $samesite,
]);
}
public function get(string $name, string $default = ''): string
{
return $_COOKIE[$name] ?? $default;
}
public function has(string $name): bool
{
return isset($_COOKIE[$name]);
}
public function delete(string $name): void
{
setcookie($name, '', [
'expires' => time() - 3600,
'path' => $this->path,
'domain' => $this->domain,
'secure' => $this->secure,
]);
unset($_COOKIE[$name]);
}
/**
* 设置 JSON Cookie
*/
public function setJson(string $name, mixed $data, int $ttl = 3600): void
{
$this->set($name, json_encode($data, JSON_UNESCAPED_UNICODE), $ttl);
}
/**
* 获取 JSON Cookie
*/
public function getJson(string $name, mixed $default = null): mixed
{
$value = $this->get($name);
if ($value === '') {
return $default;
}
$data = json_decode($value, true);
return $data ?? $default;
}
}
// 使用
$cookies = new CookieManager(secure: true, domain: 'example.com');
$cookies->set('user_pref', 'dark_theme', 86400 * 30);
$theme = $cookies->get('user_pref', 'light');
$cookies->delete('old_setting');
// JSON Cookie
$cart = ['item1' => 2, 'item2' => 1];
$cookies->setJson('shopping_cart', $cart, 86400);
$cart = $cookies->getJson('shopping_cart', []);注意事项
Cookie 大小限制
php
<?php
// Cookie 总大小限制约 4KB(每个域名)
// 超过限制会被浏览器忽略或删除旧 Cookie
// 不要在 Cookie 中存储大数据
// 不好:存储完整的用户信息
setcookie('user_info', json_encode($largeArray), time() + 3600);
// 好:只存储标识符
setcookie('user_id', '12345', time() + 3600);
// 详细信息从数据库/Session 中获取Cookie 必须在输出前设置
php
<?php
// Cookie 通过 HTTP 头设置,必须在任何输出之前
// 不好
echo "Hello";
setcookie('name', 'value'); // Warning: Cannot modify header information
// 好
setcookie('name', 'value');
echo "Hello";跨子域 Cookie
php
<?php
// 在主域设置的 Cookie 可在子域使用
setcookie('token', $token, [
'expires' => time() + 3600,
'path' => '/',
'domain' => '.example.com', // 注意前面加点
'secure' => true,
'httponly' => true,
'samesite' => 'Lax',
]);
// example.com、app.example.com、api.example.com 都可以访问最佳实践
1. Cookie 安全配置模板
php
<?php
function secureSetcookie(string $name, string $value, int $ttl = 0): bool
{
return setcookie($name, $value, [
'expires' => $ttl > 0 ? time() + $ttl : 0, // 0 = 会话 Cookie
'path' => '/',
'secure' => true, // 始终 HTTPS
'httponly' => true, // 禁止 JS 访问
'samesite' => 'Lax', // 防止 CSRF
]);
}2. 不要信任 Cookie 数据
php
<?php
// Cookie 由客户端存储和发送,可以被篡改
// 必须验证和过滤所有 Cookie 值
$userId = filter_var($_COOKIE['user_id'] ?? 0, FILTER_VALIDATE_INT);
if ($userId === 0) {
// 无效 ID
}
// 敏感操作需要重新认证进阶用法
调试与测试技巧
php
<?php
declare(strict_types=1);
// 单元测试辅助函数
function createTestResource(): mixed
{
return match (true) {
default => new stdClass(),
};
}
// 调试输出函数
function debugOutput(mixed , string = ''): void
{
= ? ": " : '';
.= print_r(, true);
fwrite(STDERR, . "\n");
}
// 性能基准测试
function benchmark(callable , int = 1000): float
{
= hrtime(true);
for ($i = 0; $i < $iterations; $i++) {
$fn();
}
return (hrtime(true) - $start) / 1e9;
}日志记录实践
php
<?php
declare(strict_types=1);
/**
* 简易日志记录器
*/
class SimpleLogger
{
private string $logFile;
private string $level = 'INFO';
public function __construct(string $logFile)
{
$this->logFile = $logFile;
}
public function info(string $message, array $context = []): void
{
$this->log('INFO', $message, $context);
}
public function warning(string $message, array $context = []): void
{
$this->log('WARNING', $message, $context);
}
public function error(string $message, array $context = []): void
{
$this->log('ERROR', $message, $context);
}
private function log(string $level, string $message, array $context): void
{
$timestamp = date('Y-m-d H:i:s');
$contextStr = $context ? ' ' . json_encode($context, JSON_UNESCAPED_UNICODE) : '';
$line = "[{$timestamp}] [{$level}] {$message}{$contextStr}\n";
file_put_contents($this->logFile, $line, FILE_APPEND | LOCK_EX);
}
}配置与环境检测
php
<?php
declare(strict_types=1);
// 环境检测工具
class EnvironmentChecker
{
public static function checkRequirements(array $requirements): array
{
$results = [];
foreach ($requirements as $name => $check) {
$results[$name] = is_callable($check) ? $check() : false;
}
return $results;
}
public static function getSystemInfo(): array
{
return [
'php_version' => PHP_VERSION,
'os' => PHP_OS,
'sapi' => PHP_SAPI,
'memory_limit' => ini_get('memory_limit'),
'max_execution_time' => ini_get('max_execution_time'),
'loaded_extensions' => get_loaded_extensions(),
];
}
}常见问题排查
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| 连接超时 | 网络问题/配置错误 | 检查配置,增加超时时间 |
| 权限不足 | 文件/目录权限 | 使用 chmod/chown 修正 |
| 性能下降 | 索引缺失/数据量大 | 添加索引,优化查询 |
| 数据不一致 | 并发冲突/事务残留 | 使用锁机制和事务 |
| 内存溢出 | 大数据集/未释放资源 | 增大内存限制,分批处理 |
故障排除步骤
- 检查错误日志和异常信息
- 确认配置和环境是否正确
- 使用调试工具逐步排查
- 参考官方文档查找已知问题
版本兼容性说明
| 功能 | 最低版本 | 说明 |
|---|---|---|
| 基础功能 | PHP 8.1 | 本文档基准版本 |
| 只读属性 | PHP 8.1 | public readonly 修饰符 |
| 枚举类型 | PHP 8.1 | enum 类型和 match 表达式 |
| Fiber | PHP 8.1 | 协程/轻量级并发 |
| 命名参数 | PHP 8.0 | foo(arg_name: value) |
| 联合类型 | PHP 8.0 | `int |
| Null 安全运算符 | PHP 8.0 | $obj?->method() |
| 析构器 promotion | PHP 8.0 | __construct(public $x) |
php
<?php
declare(strict_types=1);
// 版本兼容性检测
function ensureVersion(string $minVersion): void
{
if (version_compare(PHP_VERSION, $minVersion, '<')) {
throw new RuntimeException(
sprintf('需要 PHP %s+, 当前版本: %s', $minVersion, PHP_VERSION)
);
}
}
ensureVersion('8.1.0');