PHP http_build_cookie()函数

PHP http_build_cookie() 函数

http_build_cookie() 函数用于构建符合 HTTP 标准的 Cookie 字符串。它可以将 Cookie 参数数组转换为 Set-Cookie 请求头所需的格式字符串。

提示: 注意:http_build_cookie() 并不是 PHP 内置函数,但我们可以自定义实现这个功能。PHP 内置的 setcookie()setrawcookie() 函数直接设置 Cookie,而本函数用于生成 Cookie 字符串。
用途: 此函数常用于需要手动构建 Cookie 字符串的场景,如 API 请求、自定义 HTTP 客户端、服务器端渲染等。

语法

http_build_cookie (
    array $cookie_params
) : string

参数说明:

参数 说明 必需 默认值
$cookie_params 包含 Cookie 参数的关联数组

Cookie 参数数组支持的键名:

  • name - Cookie 名称(必需)
  • value - Cookie 值(必需)
  • expires - 过期时间(时间戳)
  • max-age - 最大生存时间(秒)
  • path - Cookie 的有效路径
  • domain - Cookie 的有效域名
  • secure - 是否仅通过 HTTPS 传输
  • httponly - 是否仅限 HTTP 访问
  • samesite - SameSite 属性(Strict/Lax/None)

函数实现

下面是一个完整的 http_build_cookie() 函数实现:

<?php
/**
 * 构建Cookie字符串
 *
 * @param array $cookie_params Cookie参数数组
 * @return string 构建好的Cookie字符串
 */
function http_build_cookie(array $cookie_params): string
{
    // 必需参数检查
    if (!isset($cookie_params['name']) || !isset($cookie_params['value'])) {
        throw new InvalidArgumentException('Cookie name and value are required');
    }

    $name = $cookie_params['name'];
    $value = $cookie_params['value'];

    // 验证名称和值
    if (!is_string($name) || !is_string($value)) {
        throw new InvalidArgumentException('Cookie name and value must be strings');
    }

    // 构建基础部分
    $cookie = urlencode($name) . '=' . urlencode($value);

    // 处理过期时间
    if (isset($cookie_params['expires'])) {
        if (is_numeric($cookie_params['expires'])) {
            $expires = (int)$cookie_params['expires'];
        } else {
            $expires = strtotime($cookie_params['expires']);
        }

        if ($expires !== false) {
            $cookie .= '; expires=' . gmdate('D, d M Y H:i:s T', $expires);
        }
    }

    // 处理max-age
    if (isset($cookie_params['max-age']) && is_numeric($cookie_params['max-age'])) {
        $cookie .= '; Max-Age=' . (int)$cookie_params['max-age'];
    }

    // 处理路径
    if (isset($cookie_params['path']) && is_string($cookie_params['path'])) {
        $cookie .= '; path=' . $cookie_params['path'];
    }

    // 处理域名
    if (isset($cookie_params['domain']) && is_string($cookie_params['domain'])) {
        $cookie .= '; domain=' . $cookie_params['domain'];
    }

    // 处理secure标志
    if (!empty($cookie_params['secure'])) {
        $cookie .= '; secure';
    }

    // 处理httponly标志
    if (!empty($cookie_params['httponly'])) {
        $cookie .= '; HttpOnly';
    }

    // 处理samesite属性
    if (isset($cookie_params['samesite']) && in_array($cookie_params['samesite'], ['Strict', 'Lax', 'None'], true)) {
        $cookie .= '; SameSite=' . $cookie_params['samesite'];

        // 如果SameSite=None,则必须设置Secure
        if ($cookie_params['samesite'] === 'None' && empty($cookie_params['secure'])) {
            $cookie .= '; secure';
        }
    }

    return $cookie;
}
?>
注意: 这是一个自定义函数实现,PHP 标准库中没有内置的 http_build_cookie() 函数。您需要在自己的项目中实现或包含此函数。

示例 1:基本用法 - 构建简单 Cookie

下面的示例演示如何使用 http_build_cookie() 函数构建基本的 Cookie 字符串。

<?php
// 引入或定义 http_build_cookie 函数
// require_once 'http_build_cookie.php';

// 构建一个简单的会话 Cookie
$simple_cookie = [
    'name' => 'session_id',
    'value' => 'abc123def456',
    'path' => '/',
    'httponly' => true
];

$cookie_string = http_build_cookie($simple_cookie);

echo "<h4>生成的 Cookie 字符串:</h4>";
echo "<div class='alert alert-info'>" . htmlspecialchars($cookie_string) . "</div>";

// 在 HTTP 响应头中使用
echo "<h4>在 HTTP 响应头中使用:</h4>";
echo "<div class='alert alert-success'>";
echo "Set-Cookie: " . htmlspecialchars($cookie_string);
echo "</div>";

// 解码查看 Cookie 信息
echo "<h4>Cookie 信息:</h4>";
echo "<ul>";
echo "<li>名称: session_id</li>";
echo "<li>值: abc123def456</li>";
echo "<li>路径: /</li>";
echo "<li>HttpOnly: 是</li>";
echo "</ul>";
?>

示例 2:使用完整参数构建安全 Cookie

下面的示例演示如何使用所有参数构建一个安全的、带有各种属性的 Cookie。

<?php
// 构建一个安全的、功能完整的 Cookie
$secure_cookie = [
    'name' => 'auth_token',
    'value' => 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9',
    'expires' => time() + (7 * 24 * 60 * 60), // 7天后过期
    'max-age' => 604800, // 7天(秒)
    'path' => '/',
    'domain' => '.example.com',
    'secure' => true, // 仅HTTPS
    'httponly' => true, // 仅HTTP访问
    'samesite' => 'Strict' // 严格的SameSite策略
];

$cookie_string = http_build_cookie($secure_cookie);

echo "<h4>生成的安全 Cookie 字符串:</h4>";
echo "<div class='alert alert-info' style='word-break: break-all;'>" . htmlspecialchars($cookie_string) . "</div>";

// 展示各个参数的作用
echo "<h4>Cookie 属性说明:</h4>";
echo "<table class='table table-bordered'>";
echo "<thead><tr><th>属性</th><th>值</th><th>说明</th></tr></thead>";
echo "<tbody>";

$attributes = [
    ['name', 'auth_token', 'Cookie名称'],
    ['value', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9', 'JWT令牌值'],
    ['expires', gmdate('Y-m-d H:i:s', time() + 604800), '过期时间(7天后)'],
    ['max-age', '604800', '最大生存时间(7天)'],
    ['path', '/', '在整个域名下有效'],
    ['domain', '.example.com', '在所有子域名下有效'],
    ['secure', 'true', '仅通过HTTPS传输'],
    ['httponly', 'true', '禁止JavaScript访问'],
    ['samesite', 'Strict', '严格的跨站请求限制']
];

foreach ($attributes as $attr) {
    echo "<tr><td><code>" . htmlspecialchars($attr[0]) . "</code></td><td>" . htmlspecialchars($attr[1]) . "</td><td>" . htmlspecialchars($attr[2]) . "</td></tr>";
}

echo "</tbody></table>";

// 比较不同SameSite设置
echo "<h4>SameSite 属性比较:</h4>";
echo "<div class='row'>";
echo "<div class='col-md-4'>";
echo "<div class='card'><div class='card-body'>";
echo "<h5 class='card-title'>Strict</h5>";
echo "<p class='card-text'>最严格,完全禁止跨站请求携带Cookie</p>";
echo "</div></div>";
echo "</div>";
echo "<div class='col-md-4'>";
echo "<div class='card'><div class='card-body'>";
echo "<h5 class='card-title'>Lax</h5>";
echo "<p class='card-text'>宽松模式,允许部分安全的跨站请求(默认)</p>";
echo "</div></div>";
echo "</div>";
echo "<div class='col-md-4'>";
echo "<div class='card'><div class='card-body'>";
echo "<h5 class='card-title'>None</h5>";
echo "<p class='card-text'>允许所有跨站请求,必须与Secure一起使用</p>";
echo "</div></div>";
echo "</div>";
echo "</div>";
?>

示例 3:批量构建多个 Cookie

下面的示例演示如何批量构建多个 Cookie,并设置到 HTTP 响应头中。

<?php
/**
 * 批量构建Cookie字符串
 *
 * @param array $cookies Cookie参数数组的数组
 * @return array Cookie字符串数组
 */
function http_build_cookies(array $cookies): array
{
    $cookie_strings = [];

    foreach ($cookies as $cookie_params) {
        try {
            $cookie_strings[] = http_build_cookie($cookie_params);
        } catch (Exception $e) {
            // 记录错误但继续处理其他Cookie
            error_log('Failed to build cookie: ' . $e->getMessage());
        }
    }

    return $cookie_strings;
}

// 定义多个Cookie
$cookies = [
    [
        'name' => 'session_id',
        'value' => 'sess_123456789',
        'path' => '/',
        'httponly' => true,
        'samesite' => 'Lax'
    ],
    [
        'name' => 'user_prefs',
        'value' => 'theme=dark&lang=zh-CN',
        'expires' => time() + (365 * 24 * 60 * 60), // 1年后过期
        'path' => '/',
        'domain' => '.example.com'
    ],
    [
        'name' => 'tracking_consent',
        'value' => 'accepted',
        'path' => '/',
        'secure' => true,
        'httponly' => false, // 允许JavaScript访问
        'samesite' => 'None'
    ]
];

// 批量构建
$cookie_strings = http_build_cookies($cookies);

echo "<h4>批量生成的 Cookie 字符串:</h4>";
foreach ($cookie_strings as $index => $cookie_string) {
    echo "<div class='alert alert-light mb-2' style='word-break: break-all;'>";
    echo "<strong>Cookie " . ($index + 1) . ":</strong> " . htmlspecialchars($cookie_string);
    echo "</div>";
}

// 模拟设置HTTP响应头
echo "<h4>在HTTP响应头中设置多个Cookie:</h4>";
echo "<div class='alert alert-success'>";
foreach ($cookie_strings as $cookie_string) {
    echo "Set-Cookie: " . htmlspecialchars($cookie_string) . "<br>";
}
echo "</div>";

// 显示Cookie统计信息
echo "<h4>Cookie 统计信息:</h4>";
echo "<ul>";
echo "<li>总共构建了 " . count($cookie_strings) . " 个Cookie</li>";
echo "<li>平均长度: " . round(array_sum(array_map('strlen', $cookie_strings)) / count($cookie_strings)) . " 字符</li>";
echo "<li>包含HttpOnly属性的Cookie: " . count(array_filter($cookies, function($c) { return !empty($c['httponly']); })) . " 个</li>";
echo "<li>包含Secure属性的Cookie: " . count(array_filter($cookies, function($c) { return !empty($c['secure']); })) . " 个</li>";
echo "</ul>";
?>

示例 4:与 PHP 内置 setcookie() 函数对比

下面的示例演示 http_build_cookie() 与 PHP 内置 setcookie() 函数的区别和联系。

<?php
// 使用 setcookie() 函数设置Cookie
function set_cookie_with_setcookie(array $params): bool
{
    return setcookie(
        $params['name'],
        $params['value'],
        $params['expires'] ?? 0,
        $params['path'] ?? '',
        $params['domain'] ?? '',
        !empty($params['secure']),
        !empty($params['httponly'])
    );
}

// 使用 http_build_cookie() 手动设置Cookie
function set_cookie_manually(array $params): void
{
    $cookie_string = http_build_cookie($params);
    header('Set-Cookie: ' . $cookie_string, false);
}

// 相同的Cookie参数
$cookie_params = [
    'name' => 'test_cookie',
    'value' => 'test_value_' . time(),
    'expires' => time() + 3600,
    'path' => '/',
    'secure' => false,
    'httponly' => true
];

echo "<h4>方法对比:</h4>";
echo "<table class='table table-bordered'>";
echo "<thead><tr><th>方法</th><th>代码示例</th><th>优点</th><th>缺点</th></tr></thead>";
echo "<tbody>";

$methods = [
    [
        'setcookie()',
        "setcookie('test_cookie', 'test_value', time() + 3600, '/', '', false, true);",
        '简单直接,PHP内置支持',
        '不能设置SameSite属性,灵活性较低'
    ],
    [
        'http_build_cookie() + header()',
        "\$cookie = http_build_cookie(\$params);
header('Set-Cookie: ' . \$cookie);", '完全控制Cookie格式,支持SameSite', '需要自定义函数,代码稍复杂' ] ]; foreach ($methods as $method) { echo "<tr>"; echo "<td><code>" . $method[0] . "</code></td>"; echo "<td><small>" . $method[1] . "</small></td>"; echo "<td>" . $method[2] . "</td>"; echo "<td>" . $method[3] . "</td>"; echo "</tr>"; } echo "</tbody></table>"; // 生成SameSite Cookie的两种方式 echo "<h4>生成带有 SameSite 属性的 Cookie:</h4>"; // PHP 7.3+ 的 setcookie() 支持 SameSite if (version_compare(PHP_VERSION, '7.3.0', '>=')) { echo "<p>PHP 7.3+ 的 setcookie() 支持 SameSite 参数:</p>"; echo "<pre><code class='language-php'>setcookie('name', 'value', [ 'expires' => time() + 3600, 'path' => '/', 'domain' => 'example.com', 'secure' => true, 'httponly' => true, 'samesite' => 'Strict' ]);</code></pre>"; } else { echo "<p class='text-warning'>您的 PHP 版本 (" . PHP_VERSION . ") 不支持 setcookie() 的 SameSite 参数。</p>"; } echo "<p>使用 http_build_cookie() 总是支持 SameSite:</p>"; echo "<pre><code class='language-php'>\$cookie = http_build_cookie([ 'name' => 'name', 'value' => 'value', 'expires' => time() + 3600, 'path' => '/', 'domain' => 'example.com', 'secure' => true, 'httponly' => true, 'samesite' => 'Strict' ]); header('Set-Cookie: ' . \$cookie);</code></pre>"; ?>

示例 5:实际应用场景 - API认证和会话管理

下面的示例演示在实际应用中使用 http_build_cookie() 进行 API 认证和会话管理。

<?php
/**
 * 用户登录处理函数
 *
 * @param string $username 用户名
 * @param string $password 密码
 * @return array 包含Cookie信息的数组
 */
function handle_user_login($username, $password)
{
    // 验证用户凭据(此处为示例,实际应用中应使用安全的验证方式)
    $user = authenticate_user($username, $password);

    if (!$user) {
        throw new Exception('Invalid credentials');
    }

    // 生成会话令牌
    $session_token = bin2hex(random_bytes(32));
    $refresh_token = bin2hex(random_bytes(32));

    // 存储会话信息(此处为示例,实际应用中应使用数据库)
    store_session($user['id'], $session_token, $refresh_token);

    // 构建会话Cookie
    $session_cookie = [
        'name' => 'session_token',
        'value' => $session_token,
        'expires' => time() + (24 * 60 * 60), // 24小时
        'path' => '/',
        'secure' => true,
        'httponly' => true,
        'samesite' => 'Strict'
    ];

    // 构建刷新令牌Cookie(更长的有效期)
    $refresh_cookie = [
        'name' => 'refresh_token',
        'value' => $refresh_token,
        'expires' => time() + (7 * 24 * 60 * 60), // 7天
        'path' => '/api/auth/refresh',
        'secure' => true,
        'httponly' => true,
        'samesite' => 'Strict'
    ];

    // 构建用户偏好设置Cookie
    $prefs_cookie = [
        'name' => 'user_preferences',
        'value' => json_encode([
            'theme' => 'dark',
            'language' => 'zh-CN',
            'timezone' => 'Asia/Shanghai'
        ]),
        'expires' => time() + (365 * 24 * 60 * 60), // 1年
        'path' => '/',
        'secure' => true,
        'httponly' => false, // 允许JavaScript访问
        'samesite' => 'Lax'
    ];

    return [
        'session' => http_build_cookie($session_cookie),
        'refresh' => http_build_cookie($refresh_cookie),
        'preferences' => http_build_cookie($prefs_cookie)
    ];
}

/**
 * 设置登录响应头
 *
 * @param array $cookies Cookie字符串数组
 */
function send_login_response(array $cookies)
{
    // 设置HTTP状态码
    http_response_code(200);

    // 设置Content-Type
    header('Content-Type: application/json');

    // 设置多个Cookie
    foreach ($cookies as $cookie_string) {
        header('Set-Cookie: ' . $cookie_string, false);
    }

    // 返回JSON响应
    echo json_encode([
        'success' => true,
        'message' => 'Login successful',
        'timestamp' => time()
    ]);
}

// 模拟用户验证函数
function authenticate_user($username, $password)
{
    // 这里应该是实际的验证逻辑
    if ($username === 'admin' && $password === 'password123') {
        return ['id' => 1, 'username' => 'admin', 'email' => 'admin@example.com'];
    }
    return false;
}

// 模拟存储会话函数
function store_session($user_id, $session_token, $refresh_token)
{
    // 这里应该是实际的存储逻辑
    return true;
}

// 示例使用
echo "<h4>用户登录处理示例:</h4>";

try {
    // 模拟用户登录
    $cookies = handle_user_login('admin', 'password123');

    echo "<div class='alert alert-success'>";
    echo "<h5>登录成功!生成的Cookie:</h5>";

    foreach ($cookies as $type => $cookie_string) {
        echo "<div class='mb-2'>";
        echo "<strong>" . ucfirst($type) . " Cookie:</strong><br>";
        echo "<code style='word-break: break-all; font-size: 0.9em;'>" . htmlspecialchars($cookie_string) . "</code>";
        echo "</div>";
    }

    echo "</div>";

    // 显示将会设置的响应头
    echo "<h5>将会设置的HTTP响应头:</h5>";
    echo "<div class='alert alert-info'>";
    foreach ($cookies as $type => $cookie_string) {
        echo "Set-Cookie: " . htmlspecialchars($cookie_string) . "<br>";
    }
    echo "</div>";

} catch (Exception $e) {
    echo "<div class='alert alert-danger'>登录失败: " . htmlspecialchars($e->getMessage()) . "</div>";
}

// Cookie安全最佳实践
echo "<h4 class='mt-4'>Cookie安全最佳实践:</h4>";
echo "<ul>";
echo "<li><strong>总是使用HttpOnly</strong>:防止XSS攻击窃取Cookie</li>";
echo "<li><strong>生产环境使用Secure</strong>:仅通过HTTPS传输Cookie</li>";
echo "<li><strong>设置适当的SameSite</strong>:防止CSRF攻击</li>";
echo "<li><strong>合理设置过期时间</strong>:会话Cookie应较短,刷新令牌可较长</li>";
echo "<li><strong>使用强随机令牌</strong>:Cookie值应是不可预测的</li>";
echo "<li><strong>限制路径和域名</strong>:减少Cookie的暴露范围</li>";
echo "</ul>";
?>

注意事项和常见问题

  • 非内置函数http_build_cookie() 不是 PHP 内置函数,需要自定义实现。
  • URL编码:Cookie 名称和值需要正确进行 URL 编码,本函数使用 urlencode() 处理。
  • 过期时间格式:HTTP Cookie 的过期时间必须使用 GMT 格式。
  • SameSite=None 要求:当 SameSite 设置为 None 时,必须同时设置 Secure 属性。
  • 头部注入:确保 Cookie 值不包含换行符等可能用于 HTTP 头部注入的字符。
  • 浏览器限制:不同浏览器对 Cookie 大小和数量有限制(通常每个 Cookie 4KB,每个域名 50-150 个)。
  • 时区处理:过期时间应使用 GMT/UTC 时间,确保跨时区一致性。
  • 错误处理:本函数在参数错误时抛出异常,调用时应适当处理。
  • 性能考虑:对于大量 Cookie,手动构建可能比 setcookie() 稍慢,但差异通常不明显。

相关函数