針對「僅需記錄商品類型與編號、不涉及數量」的簡易購物車場景,本文提供一個基於 Cookie 的純 PHP 實現。代碼結構清晰,支援新增、查詢、刪除、清空操作,適合中小型專案快速整合或作為教學參考。注意:Cookie 容量限制 4KB,商品數量較大時建議改用 Session + 資料庫方案。
Cookie 值格式:type1:id1,id2|type2:id3,id4|type3:id5
| 分隔: 分隔, 分隔<?php
class Cart
{
private const COOKIE_NAME = 'cart';
private const COOKIE_EXPIRE = 86400 * 30; // 30 天
/**
* 新增商品到購物車
* @param string $type 商品類型
* @param string|int $id 商品編號
*/
public function add(string $type, $id): void
{
$cartStr = $_COOKIE[self::COOKIE_NAME] ?? '';
if ($cartStr === '') {
// 首次新增
$cartStr = "{$type}:{$id}";
} else {
$typeExists = false;
$parts = explode('|', $cartStr);
foreach ($parts as $i => $part) {
[$existingType, $idList] = explode(':', $part, 2);
if ($existingType === $type) {
$typeExists = true;
$ids = explode(',', $idList);
if (!in_array((string)$id, $ids, true)) {
$ids[] = (string)$id;
$parts[$i] = $type . ':' . implode(',', $ids);
}
break;
}
}
if (!$typeExists) {
$parts[] = "{$type}:{$id}";
}
$cartStr = implode('|', $parts);
}
setcookie(self::COOKIE_NAME, $cartStr, time() + self::COOKIE_EXPIRE, '/');
}
/**
* 獲取指定類型的商品 ID 列表
* @return array 數字索引陣列,不存在返回空陣列
*/
public function getIds(string $type): array
{
$cartStr = $_COOKIE[self::COOKIE_NAME] ?? '';
if ($cartStr === '') return [];
$pattern = '/' . preg_quote($type, '/') . ':([^|]+)/';
if (preg_match($pattern, $cartStr, $matches)) {
return explode(',', $matches[1]);
}
return [];
}
/**
* 刪除指定類型下的某個商品 ID
*/
public function remove(string $type, $id): void
{
$cartStr = $_COOKIE[self::COOKIE_NAME] ?? '';
if ($cartStr === '') return;
$parts = explode('|', $cartStr);
$newParts = [];
foreach ($parts as $part) {
[$existingType, $idList] = explode(':', $part, 2);
if ($existingType !== $type) {
$newParts[] = $part;
continue;
}
$ids = explode(',', $idList);
$ids = array_filter($ids, fn($v) => $v !== (string)$id);
if (!empty($ids)) {
$newParts[] = $type . ':' . implode(',', $ids);
}
// 若該類型無剩餘 ID,直接不加入 newParts(相當於刪除該類型)
}
$newCartStr = implode('|', $newParts);
setcookie(self::COOKIE_NAME, $newCartStr, time() + self::COOKIE_EXPIRE, '/');
}
/**
* 清空購物車
*/
public function clear(): void
{
setcookie(self::COOKIE_NAME, '', time() - 3600, '/');
}
/**
* 獲取完整購物車結構(用於調試/前端渲染)
* @return array ['type' => ['id1', 'id2', ...], ...]
*/
public function all(): array
{
$cartStr = $_COOKIE[self::COOKIE_NAME] ?? '';
if ($cartStr === '') return [];
$result = [];
foreach (explode('|', $cartStr) as $part) {
[$type, $idList] = explode(':', $part, 2);
$result[$type] = explode(',', $idList);
}
return $result;
}
}
<?php
require_once 'Cart.php';
$cart = new Cart();
// 新增商品
$cart->add('food', 101);
$cart->add('food', 102);
$cart->add('drink', 201);
// 查詢
var_dump($cart->getIds('food')); // ['101', '102']
var_dump($cart->all());
// 刪除單個商品
$cart->remove('food', 101);
// 清空
$cart->clear();
| 需求 | 實現方向 |
|---|---|
| 支援商品數量 | ID 列表改為 id:qty 格式,或改用 JSON 結構 {type: {id: qty}} |
| 突破 4KB 限制 | Cookie 僅存 cart_token,資料落地 Redis/資料庫,Key 為 token |
| 多伺服器同步 | 必須使用集中式存儲(Redis/DB),Cookie 僅作識別 |
| 防篡改 | 簽名(HMAC)或加密(AES-GCM)Cookie 值 |
HttpOnly、Secure(HTTPS 環境)、SameSite=Lax 降低 XSS/CSRF 風險。