# Cookie 类

HTTP Cookie （网页 Cookie、浏览器 Cookie）是服务器发送到用户浏览器的一小段数据。浏览器可存储该数据，并在后续向同一服务器发送请求时将其回传。通常用于判断两个请求是否来自同一浏览器，例如保持用户登录状态，为无状态的 HTTP 协议记录状态信息。

Cookie 主要用于三种场景：

- 会话管理：登录状态、购物车、游戏分数或服务器需要记住的其他信息

- 个性化：用户偏好、主题和其他设置

- 追踪：记录和分析用户行为

为高效地向浏览器发送 Cookie，CodeIgniter 提供 `CodeIgniter\Cookie\Cookie` 类来抽象 Cookie 交互操作。

## 创建 Cookie

目前可通过四种方式创建新的 `Cookie` 值对象。

    <?php

    use CodeIgniter\Cookie\Cookie;
    use DateTime;

    // Using the constructor
    $cookie = new Cookie(
        'remember_token',
        'f699c7fd18a8e082d0228932f3acd40e1ef5ef92efcedda32842a211d62f0aa6',
        [
            'expires'  => new DateTime('+2 hours'),
            'prefix'   => '__Secure-',
            'path'     => '/',
            'domain'   => '',
            'secure'   => true,
            'httponly' => true,
            'raw'      => false,
            'samesite' => Cookie::SAMESITE_LAX,
        ],
    );

    // Supplying a Set-Cookie header string
    $cookie = Cookie::fromHeaderString(
        'remember_token=f699c7fd18a8e082d0228932f3acd40e1ef5ef92efcedda32842a211d62f0aa6; Path=/; Secure; HttpOnly; SameSite=Lax',
        false, // raw
    );

    // Using the fluent builder interface
    $cookie = (new Cookie('remember_token'))
        ->withValue('f699c7fd18a8e082d0228932f3acd40e1ef5ef92efcedda32842a211d62f0aa6')
        ->withPrefix('__Secure-')
        ->withExpires(new DateTime('+2 hours'))
        ->withPath('/')
        ->withDomain('')
        ->withSecure(true)
        ->withHTTPOnly(true)
        ->withSameSite(Cookie::SAMESITE_LAX);

    // Using the global function `cookie` which implicitly calls `new Cookie()`
    $cookie = cookie('remember_token', 'f699c7fd18a8e082d0228932f3acd40e1ef5ef92efcedda32842a211d62f0aa6');

构造 `Cookie` 对象时，仅需 `name` 属性，其余均为可选。若未修改可选属性，将使用 `Cookie` 类中保存的默认值。

### 覆盖默认值

要覆盖类中当前存储的默认值，可向静态方法 `Cookie::setDefaults()` 传入 `Config\Cookie` 实例或包含默认值的数组。

    <?php

    use CodeIgniter\Cookie\Cookie;
    use Config\Cookie as CookieConfig;

    // pass in a Config\Cookie instance before constructing a Cookie class
    Cookie::setDefaults(config(CookieConfig::class));
    $cookie = new Cookie('login_token');

    // pass in an array of defaults
    $myDefaults = [
        'expires'  => 0,
        'samesite' => Cookie::SAMESITE_STRICT,
    ];
    Cookie::setDefaults($myDefaults);
    $cookie = new Cookie('login_token');

向 `Cookie::setDefaults()` 传入 `Config\Cookie` 实例或数组会覆盖当前默认值，并在传入新默认值前一直生效。

#### 临时更改默认值

若不希望此行为生效，仅需临时更改默认值，可利用 `Cookie::setDefaults()` 返回旧默认值数组的特性。

    <?php

    use CodeIgniter\Cookie\Cookie;
    use Config\Cookie as CookieConfig;

    $oldDefaults = Cookie::setDefaults(config(CookieConfig::class));
    $cookie      = new Cookie('my_token', 'muffins');

    // return the old defaults
    Cookie::setDefaults($oldDefaults);

## 访问 Cookie 属性

实例化后，可通过 getter 方法轻松访问 `Cookie` 的属性。

    <?php

    use CodeIgniter\Cookie\Cookie;
    use DateTime;
    use DateTimeZone;

    $cookie = new Cookie(
        'remember_token',
        'f699c7fd18a8e082d0228932f3acd40e1ef5ef92efcedda32842a211d62f0aa6',
        [
            'expires'  => new DateTime('2025-02-14 00:00:00', new DateTimeZone('UTC')),
            'prefix'   => '__Secure-',
            'path'     => '/',
            'domain'   => '',
            'secure'   => true,
            'httponly' => true,
            'raw'      => false,
            'samesite' => Cookie::SAMESITE_LAX,
        ],
    );

    $cookie->getName();             // 'remember_token'
    $cookie->getPrefix();           // '__Secure-'
    $cookie->getPrefixedName();     // '__Secure-remember_token'
    $cookie->getExpiresTimestamp(); // UNIX timestamp
    $cookie->getExpiresString();    // 'Fri, 14 Feb 2025 00:00:00 GMT'
    $cookie->isExpired();           // false
    $cookie->getMaxAge();           // the difference from time() to expires
    $cookie->isRaw();               // false
    $cookie->isSecure();            // true
    $cookie->getPath();             // '/'
    $cookie->getDomain();           // ''
    $cookie->isHTTPOnly();          // true
    $cookie->getSameSite();         // 'Lax'

    // additional getter
    $cookie->getId(); // '__Secure-remember_token;;/'

    // when using `setcookie()`'s alternative signature on PHP 7.3+
    // you can easily use the `getOptions()` method to supply the
    // $options parameter
    $cookie->getOptions();

## 不可变 Cookie

新的 `Cookie` 实例是 HTTP Cookie 的不可变值对象表示。因其不可变性，修改实例的任意属性不会影响原实例，修改操作 始终 返回新实例，需保留该新实例才能使用。

    <?php

    use CodeIgniter\Cookie\Cookie;

    $cookie = new Cookie('login_token', 'admin');
    $cookie->getName(); // 'login_token'

    $cookie->withName('remember_token');
    $cookie->getName(); // 'login_token'

    $new = $cookie->withName('remember_token');
    $new->getName(); // 'remember_token'

## 验证 Cookie 属性

HTTP Cookie 受多项规范约束，必须遵守才能被浏览器接受。因此创建或修改 `Cookie` 的某些属性时，会进行验证以确保符合规范。

若存在违规情况，将抛出 `CookieException` 异常。

### 验证 Name 属性

Cookie 名称可使用任意 US-ASCII 字符，但以下字符除外：

- 控制字符

- 空格或制表符

- 分隔符，如 `( ) < > @ , ; : \ " / [ ] ? = { }`

若将 `$raw` 参数设为 `true`，验证将更为严格。因为 PHP 的 setcookie() 和 setrawcookie() 会拒绝名称无效的 Cookie。此外，Cookie 名称不能为空字符串。

### 验证 Prefix 属性

使用 `__Secure-` 前缀时，Cookie 必须设置 `$secure` 标志为 `true`。若使用 `__Host-` 前缀，Cookie 必须满足以下条件：

- `$secure` 标志设为 `true`

- `$domain` 为空

- `$path` 必须为 `/`

### 验证 SameSite 属性

SameSite 属性接受三个值：

- Lax：常规跨站点子请求（如向第三方网站加载图片或框架）不发送 Cookie，但用户导航到源站点时（如点击链接）会发送。

- Strict：Cookie 仅在第一方上下文中发送，不会随第三方网站发起的请求一起发送。

- None：Cookie 在所有上下文中发送，即响应第一方和跨源请求时均会发送。

CodeIgniter 允许将 SameSite 属性设为空字符串。传入空字符串时，将使用 `Cookie` 类中保存的默认 SameSite 设置。可通过上述 `Cookie::setDefaults()` 方法更改默认 SameSite 设置。

近期 Cookie 规范已变更，现代浏览器若未提供 SameSite 值，则默认使用 `Lax`。若将 SameSite 设为空字符串且默认 SameSite 也为空字符串，Cookie 将被赋予 `Lax` 值。

若 SameSite 设为 `None`，需确保 `Secure` 也设为 `true`。

写入 SameSite 属性时，`Cookie` 类不区分大小写地接受以上任一值。也可使用类常量简化操作。

    <?php

    use CodeIgniter\Cookie\Cookie;

    Cookie::SAMESITE_LAX;    // 'Lax'
    Cookie::SAMESITE_STRICT; // 'Strict'
    Cookie::SAMESITE_NONE;   // 'None'

## 发送 Cookie

在 Response 对象的 `CookieStore` 中设置 `Cookie` 对象，框架会自动发送这些 Cookie。

使用 `CodeIgniter\HTTP\Response::setCookie()` 进行设置：

    <?php

    use CodeIgniter\Cookie\Cookie;

    $response = service('response');

    $cookie = new Cookie(
        'remember_token',
        'f699c7fd18a8e082d0228932f3acd40e1ef5ef92efcedda32842a211d62f0aa6',
        [
            'max-age' => 3600 * 2, // Expires in 2 hours
        ],
    );

    $response->setCookie($cookie);

也可使用 `set_cookie()` 辅助函数：

    <?php

    use CodeIgniter\Cookie\Cookie;

    helper('cookie');

    $cookie = new Cookie(
        'remember_token',
        'f699c7fd18a8e082d0228932f3acd40e1ef5ef92efcedda32842a211d62f0aa6',
        [
            'max-age' => 3600 * 2, // Expires in 2 hours
        ],
    );

    set_cookie($cookie);

## 使用 Cookie Store

备注

通常无需直接使用 CookieStore。

`CookieStore` 类表示 `Cookie` 对象的不可变集合。

### 从 Response 获取 Store

可从当前 `Response` 对象访问 `CookieStore` 实例。

    <?php

    $cookieStore = service('response')->getCookieStore();

### 创建 CookieStore

CodeIgniter 提供另外三种方式创建 `CookieStore` 新实例。

    <?php

    use CodeIgniter\Cookie\Cookie;
    use CodeIgniter\Cookie\CookieStore;

    // Passing an array of `Cookie` objects in the constructor
    $store = new CookieStore([
        new Cookie('login_token'),
        new Cookie('remember_token'),
    ]);

    // Passing an array of `Set-Cookie` header strings
    $store = CookieStore::fromCookieHeaders([
        'remember_token=me; Path=/; SameSite=Lax',
        'login_token=admin; Path=/; SameSite=Lax',
    ]);

    // using the global `cookies` function
    $store = cookies([new Cookie('login_token')], false);

    // retrieving the `CookieStore` instance saved in our current `Response` object
    $store = cookies();

备注

使用全局 `cookies()` 函数时，仅当第二个参数 `$getGlobal` 设为 `false` 时才会使用传入的 `Cookie` 数组。

### 检查 Store 中的 Cookie

检查 `CookieStore` 实例中是否存在 `Cookie` 对象，可使用以下方式：

    <?php

    use CodeIgniter\Cookie\Cookie;
    use CodeIgniter\Cookie\CookieStore;

    // check if cookie is in the current cookie collection
    $store = new CookieStore([
        new Cookie('login_token'),
        new Cookie('remember_token'),
    ]);
    $store->has('login_token');

    // check if cookie is in the current Response's cookie collection
    cookies()->has('login_token');
    service('response')->hasCookie('remember_token');

    // using the cookie helper to check the current Response
    // not available to v4.1.1 and lower
    helper('cookie');
    has_cookie('login_token');

### 获取 Store 中的 Cookie

从 Cookie 集合中获取 `Cookie` 实例非常简单：

    <?php

    use CodeIgniter\Cookie\Cookie;
    use CodeIgniter\Cookie\CookieStore;
    use Config\Services;

    // getting cookie in the current cookie collection
    $store = new CookieStore([
        new Cookie('login_token'),
        new Cookie('remember_token'),
    ]);
    $store->get('login_token');

    // getting cookie in the current Response's cookie collection
    cookies()->get('login_token');
    service('response')->getCookie('remember_token');

    // using the cookie helper to get cookie from the Response's cookie collection
    helper('cookie');
    get_cookie('remember_token');

直接从 `CookieStore` 获取 `Cookie` 实例时，无效名称将抛出 `CookieException` 异常。

    <?php

    // throws CookieException
    $store->get('unknown_cookie');

从当前 `Response` 的 Cookie 集合获取 `Cookie` 实例时，无效名称仅返回 `null`。

    <?php

    cookies()->get('unknown_cookie'); // null

从 `Response` 获取 Cookie 时若未提供参数，将显示 Store 中的所有 `Cookie` 对象。

    <?php

    cookies()->display(); // array of Cookie objects

    // or even from the Response
    service('response')->getCookies();

备注

辅助函数 `get_cookie()` 从当前 `Request` 对象而非 `Response` 获取 Cookie。该函数检查 `$_COOKIE` 数组中是否设置了该 Cookie 并立即获取。

### 在 Store 中添加/移除 Cookie

如前所述，`CookieStore` 对象不可变。需保存修改后的实例才能继续操作，原实例保持不变。

    <?php

    use CodeIgniter\Cookie\Cookie;
    use CodeIgniter\Cookie\CookieStore;

    $store = new CookieStore([
        new Cookie('login_token'),
        new Cookie('remember_token'),
    ]);

    // adding a new Cookie instance
    $new = $store->put(new Cookie('admin_token', 'yes'));

    // removing a Cookie instance
    $new = $store->remove('login_token');

备注

从 Store 移除 Cookie 不会 从浏览器中删除。若要从浏览器删除 Cookie，必须将同名空值 Cookie 放入 Store。

与当前 `Response` 对象中的 Store Cookie 交互时，可安全地添加或删除 Cookie，无需担心 Cookie 集合的不可变性。`Response` 对象会用修改后的实例替换原实例。

    <?php

    service('response')->setCookie('admin_token', 'yes');
    service('response')->deleteCookie('login_token');

    // using the cookie helper
    helper('cookie');
    set_cookie('admin_token', 'yes');
    delete_cookie('login_token');

### 发送 Store 中的 Cookie

重要

此方法自 4.1.6 版本起已弃用，并在 4.6.0 版本中移除。

通常无需手动发送 Cookie，CodeIgniter 会自动处理。但若确实需要手动发送，可使用 `dispatch` 方法。与发送其他标头一样，需先通过 `headers_sent()` 检查标头是否尚未发送。

    <?php

    use CodeIgniter\Cookie\Cookie;
    use CodeIgniter\Cookie\CookieStore;

    $store = new CookieStore([
        new Cookie('login_token'),
        new Cookie('remember_token'),
    ]);

    $store->dispatch(); // After dispatch, the collection is now empty.

## Cookie 个性化设置

`Cookie` 类内置合理的默认值，确保 Cookie 对象创建顺畅。但可通过修改 app/Config/Cookie.php 文件中的 `Config\Cookie` 类来定义自定义设置。

设置 | 选项/类型 | 默认值 | 描述
--- | --- | --- | ---
$prefix | `string` | `''` | 添加到 Cookie 名称的前缀。
$expires | `DateTimeInterface|string|int` | `0` | 过期时间戳。
$path | `string` | `/` | Cookie 的路径属性。
$domain | `string` | `''` | Cookie 的域名属性（可带尾部斜杠）。
$secure | `true`/`false` | `false` | 是否仅通过 HTTPS 发送。
$httponly | `true`/`false` | `true` | 是否对 JavaScript 不可访问。
$samesite | `Lax`/`None`/`Strict` | `Lax` | SameSite 属性。
$raw | `true`/`false` | `false` | 是否使用 `setrawcookie()` 发送。

运行时可使用 `Cookie::setDefaults()` 方法手动提供新的默认值。

## 类参考

class CodeIgniter\Cookie\Cookie
  static setDefaults([$config = []])
    参数:

      - $config (`\Config\Cookie|array`) -- 配置数组或实例

    返回类型:

      `array`

    返回:

      旧的默认值

    通过注入 `Config\Cookie` 配置或数组的值，为 Cookie 实例设置默认属性。

  static fromHeaderString(string $header[, bool $raw = false])
    参数:

      - $header (`string`) -- `Set-Cookie` 标头字符串

      - $raw (`bool`) -- 是否不对 Cookie 进行 URL 编码并通过 `setrawcookie()` 发送

    返回类型:

      `Cookie`

    返回:

      `Cookie` 实例

    Throws:

      `CookieException`

    从 `Set-Cookie` 标头创建新的 Cookie 实例。

  __construct(string $name[, string $value = ''[, array $options = []]])
    参数:

      - $name (`string`) -- Cookie 名称

      - $value (`string`) -- Cookie 值

      - $options (`array`) -- Cookie 选项

    返回类型:

      `Cookie`

    返回:

      `Cookie` 实例

    Throws:

      `CookieException`

    构造新的 Cookie 实例。

  getId()
    返回类型:

      `string`

    返回:

      Cookie 集合中索引使用的 ID。

  getPrefix() → string
  getName() → string
  getPrefixedName() → string
  getValue() → string
  getExpiresTimestamp() → int
  getExpiresString() → string
  isExpired() → bool
  getMaxAge() → int
  getDomain() → string
  getPath() → string
  isSecure() → bool
  isHTTPOnly() → bool
  getSameSite() → string
  isRaw() → bool
  getOptions() → array
  withRaw([bool $raw = true])
    参数:

      - $raw (`bool`)

    返回类型:

      `Cookie`

    返回:

      新的 `Cookie` 实例

    创建带有更新 URL 编码选项的新 Cookie。

  withPrefix([string $prefix = ''])
    参数:

      - $prefix (`string`)

    返回类型:

      `Cookie`

    返回:

      新的 `Cookie` 实例

    创建带有新前缀的新 Cookie。

  withName(string $name)
    参数:

      - $name (`string`)

    返回类型:

      `Cookie`

    返回:

      新的 `Cookie` 实例

    创建带有新名称的新 Cookie。

  withValue(string $value)
    参数:

      - $value (`string`)

    返回类型:

      `Cookie`

    返回:

      新的 `Cookie` 实例

    创建带有新值的新 Cookie。

  withExpires($expires)
    参数:

      - $expires (`DateTimeInterface|string|int`)

    返回类型:

      `Cookie`

    返回:

      新的 `Cookie` 实例

    创建带有新过期时间的新 Cookie。

  withExpired()
    返回类型:

      `Cookie`

    返回:

      新的 `Cookie` 实例

    创建将从浏览器中过期的新 Cookie。

  withNeverExpiring()

    重要

    此方法自 4.2.6 版本起已弃用，并在 4.6.0 版本中移除。

    参数:

      - $name (`string`)

    返回类型:

      `Cookie`

    返回:

      新的 `Cookie` 实例

    创建几乎永不过期的新 Cookie。

  withDomain(?string $domain)
    参数:

      - $domain (`string|null`)

    返回类型:

      `Cookie`

    返回:

      新的 `Cookie` 实例

    创建带有新域名的新 Cookie。

  withPath(?string $path)
    参数:

      - $path (`string|null`)

    返回类型:

      `Cookie`

    返回:

      新的 `Cookie` 实例

    创建带有新路径的新 Cookie。

  withSecure([bool $secure = true])
    参数:

      - $secure (`bool`)

    返回类型:

      `Cookie`

    返回:

      新的 `Cookie` 实例

    创建带有新 "Secure" 属性的新 Cookie。

  withHTTPOnly([bool $httponly = true])
    参数:

      - $httponly (`bool`)

    返回类型:

      `Cookie`

    返回:

      新的 `Cookie` 实例

    创建带有新 "HttpOnly" 属性的新 Cookie。

  withSameSite(string $samesite)
    参数:

      - $samesite (`string`)

    返回类型:

      `Cookie`

    返回:

      新的 `Cookie` 实例

    创建带有新 "SameSite" 属性的新 Cookie。

  toHeaderString()
    返回类型:

      `string`

    返回:

      返回可作为标头字符串传递的字符串表示。

  toArray()
    返回类型:

      `array`

    返回:

      返回 Cookie 实例的数组表示。

class CodeIgniter\Cookie\CookieStore
  static fromCookieHeaders(array $headers[, bool $raw = false])
    参数:

      - $header (`array`) -- `Set-Cookie` 标头数组

      - $raw (`bool`) -- 是否不使用 URL 编码

    返回类型:

      `CookieStore`

    返回:

      `CookieStore` 实例

    Throws:

      `CookieException`

    从 `Set-Cookie` 标头数组创建 CookieStore。

  __construct(array $cookies)
    参数:

      - $cookies (`array`) -- `Cookie` 对象数组

    返回类型:

      `CookieStore`

    返回:

      `CookieStore` 实例

    Throws:

      `CookieException`

  has(string $name[, string $prefix = ''[, ?string $value = null]]) → bool
    参数:

      - $name (`string`) -- Cookie 名称

      - $prefix (`string`) -- Cookie 前缀

      - $value (`string|null`) -- Cookie 值

    返回类型:

      `bool`

    返回:

      检查集合中是否存在由名称和前缀标识的 `Cookie` 对象。

  get(string $name[, string $prefix = '']) → Cookie
    参数:

      - $name (`string`) -- Cookie 名称

      - $prefix (`string`) -- Cookie 前缀

    返回类型:

      `Cookie`

    返回:

      按名称和前缀检索 Cookie 实例。

    Throws:

      `CookieException`

  put(Cookie $cookie) → CookieStore
    参数:

      - $cookie (`Cookie`) -- Cookie 对象

    返回类型:

      `CookieStore`

    返回:

      新的 `CookieStore` 实例

    存储新 Cookie 并返回新集合，原集合保持不变。

  remove(string $name[, string $prefix = '']) → CookieStore
    参数:

      - $name (`string`) -- Cookie 名称

      - $prefix (`string`) -- Cookie 前缀

    返回类型:

      `CookieStore`

    返回:

      新的 `CookieStore` 实例

    从集合中移除 Cookie 并返回更新后的集合，原集合保持不变。

  dispatch() → void

    重要

    此方法自 4.1.6 版本起已弃用，并在 4.6.0 版本中移除。

    返回类型:

      `void`

    发送 Store 中的所有 Cookie。

  display() → array
    返回类型:

      `array`

    返回:

      返回 Store 中的所有 Cookie 实例。

  clear() → void
    返回类型:

      `void`

    清空 Cookie 集合。