首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 基於 Cookie 的購物車類完整實現與最佳實踐

     編輯:PHP 綜合知識

前言

購物車是電商系統核心模組之一。對於中小型項目或無服務端 Session 存儲條件的場景,基於 Cookie 的客戶端存儲方案具有部署簡單、無需資料庫、天然支援分佈式等優勢。本文提供一個功能完整、結構清晰、生產可用的 CartApi 類,支援商品增刪改查、多規格價格、數量統計、自動序列化持久化等特性。

核心設計決策

決策點方案理由
存儲介質 Cookie (序列化陣列) 無服務端狀態,天然水平擴展
數據結構 二維關聯陣列 [字段][商品ID] 單次遍歷即可完成多種統計,記憶體效率高
序列化格式 PHP serialize() 保留類型信息,支援物件,比 JSON 更緊湊
過期策略 可配置 TTL,預設 86400 秒 (1天) 平衡用戶體驗與 Cookie 體積限制 (4KB)
併發處理 整體覆寫模式 Cookie 無鎖機制,接受極低概率的覆寫丟失

完整類實現

<?php
/**
 * Cookie 購物車核心類
 * 
 * 特性:
 * - 支援多規格價格 (price1/price2/price3)
 * - 自動合併同款商品數量
 * - 靈活的修改標誌 (增/減/設定/移除)
 * - 價格統計與數量統計
 * - 可配置過期時間
 */
class CartApi
{
    // 內部數據結構:
    // [0] => [id => id]
    // [1] => [id => name]
    // [2] => [id => price1]
    // [3] => [id => price2]
    // [4] => [id => price3]
    // [5] => [id => count]
    // [6] => [id => image]
    private $cartArray = [];
    private $cartCount = 0;
    public $expires = 86400; // 秒,0 表示瀏覽器關閉即失效

    public function __construct(
        $id = "", $name = "", $price1 = "", $price2 = "", $price3 = "",
        $count = "", $image = "", $expires = 86400
    ) {
        if ($id !== "" && is_numeric($id)) {
            $this->expires = $expires;
            $this->addCart($id, $name, $price1, $price2, $price3, $count, $image);
        }
    }

    /**
     * 添加商品到購物車
     * @return bool false=商品已存在(已合併數量) true=新增成功
     */
    public function addCart($id, $name, $price1, $price2, $price3, $count, $image)
    {
        $this->cartArray = $this->cartView();
        if ($this->checkItem($id)) {
            $this->modifyCart($id, $count, 0); // 0 = 增加數量
            return false;
        }
        
        $this->cartArray[0][$id] = $id;
        $this->cartArray[1][$id] = $name;
        $this->cartArray[2][$id] = $price1;
        $this->cartArray[3][$id] = $price2;
        $this->cartArray[4][$id] = $price3;
        $this->cartArray[5][$id] = (int)$count;
        $this->cartArray[6][$id] = $image;
        $this->save();
        return true;
    }

    /**
     * 修改購物車商品
     * @param int $flag 0:增加 1:減少 2:設定數量(0=移除) 3:移除
     * @return bool
     */
    public function modifyCart($id, $count, $flag = 0)
    {
        $this->cartArray = $this->cartView();
        $arr = &$this->cartArray; // 參考操作
        
        if (!is_array($arr[0]) || $id < 1) return false;
        
        foreach ($arr[0] as $itemId) {
            if ($itemId === $id) {
                switch ($flag) {
                    case 0: // 增加
                        $arr[5][$id] += $count;
                        break;
                    case 1: // 減少
                        $arr[5][$id] -= $count;
                        if ($arr[5][$id] <= 0) $flag = 3; // 數量歸零自動移除
                        break;
                    case 2: // 設定具體數量
                        if ($count == 0) {
                            $flag = 3;
                        } else {
                            $arr[5][$id] = $count;
                        }
                        break;
                    case 3: // 移除/清空
                    default:
                        unset($arr[0][$id], $arr[1][$id], $arr[2][$id],
                              $arr[3][$id], $arr[4][$id], $arr[5][$id], $arr[6][$id]);
                        break;
                }
                $this->save();
                return true;
            }
        }
        return false;
    }

    /** 清空整個購物車 */
    public function removeAll()
    {
        $this->cartArray = [];
        $this->save();
    }

    /** 獲取完整購物車數據 (二維陣列) */
    public function cartView()
    {
        $cookie = stripslashes($_COOKIE['cartapi'] ?? '');
        if (!$cookie) return false;
        return @unserialize($cookie) ?: false;
    }

    /** 檢查購物車是否有商品 */
    public function checkCart()
    {
        $arr = $this->cartView();
        return is_array($arr[0] ?? null) && count($arr[0]) > 0;
    }

    /**
     * 價格統計
     * @return array [0=>price1總計, 1=>price2總計, 2=>price3總計, 3=>總數量]
     */
    public function countPrice()
    {
        $arr = $this->cartArray = $this->cartView();
        $out = [0, 0, 0, 0];
        
        if (is_array($arr[0])) {
            foreach ($arr[0] as $key => $val) {
                $qty = (int)$arr[5][$key];
                $out[0] += (float)$arr[2][$key] * $qty;
                $out[1] += (float)$arr[3][$key] * $qty;
                $out[2] += (float)$arr[4][$key] * $qty;
                $out[3] += $qty;
            }
        }
        return $out;
    }

    /** 獲取商品種類數 */
    public function cartCount()
    {
        $arr = $this->cartView();
        $this->cartCount = is_array($arr[0]) ? count($arr[0]) : 0;
        return $this->cartCount;
    }

    /** 持久化到 Cookie */
    public function save()
    {
        $serialized = serialize($this->cartArray);
        // 檢查 Cookie 大小限制 (約 4KB)
        if (strlen($serialized) > 3800) {
            trigger_error('購物車數據超過 Cookie 大小限制', E_USER_WARNING);
        }
        setcookie("cartapi", $serialized, time() + $this->expires, '/', '', false, true);
    }

    /** 內部檢查商品是否存在 */
    private function checkItem($id)
    {
        $arr = $this->cartArray;
        if (!is_array($arr[0])) return false;
        return in_array($id, $arr[0], true);
    }
}

使用示例

1. 添加商品 (商品詳情頁)

<?php
require_once 'CartApi.php';

// 實例化即可添加 (構造函數自動調用 addCart)
$cart = new CartApi(
    $_POST['goods_id'],      // id
    $_POST['goods_name'],    // name
    $_POST['price1'],        // 價格1 (如會員價)
    $_POST['price2'],        // 價格2 (如批發價)
    $_POST['price3'],        // 價格3 (如零售價)
    $_POST['quantity'] ?? 1, // 數量
    $_POST['image_url'],     // 圖片
    86400 * 7                // 保存 7 天
);

// 重定向防止表單重複提交
header('Location: cart.php');
exit;

2. 購物車列表頁

<?php
require_once 'CartApi.php';
$cart = new CartApi();

if (!$cart->checkCart()) {
    echo '

購物車是空的

'; exit; } $items = $cart->cartView(); $totals = $cart->countPrice(); // [price1總價, price2總價, price3總價, 總數量] ?> $val): ?>
商品單價數量小計操作
¥
¥ 移除
合計 件 ¥ 去結算

3. 操作處理頁

<?php
// cart_action.php
require_once 'CartApi.php';
$cart = new CartApi();
$action = $_GET['action'] ?? $_POST['action'] ?? '';
$id = (int)($_GET['id'] ?? $_POST['id'] ?? 0);

switch ($action) {
    case 'update':
        $count = (int)($_POST['count'] ?? 0);
        $cart->modifyCart($id, $count, 2); // 2 = 設定具體數量
        break;
    case 'remove':
        $cart->modifyCart($id, 0, 3); // 3 = 移除
        break;
    case 'clear':
        $cart->removeAll();
        break;
}

header('Location: ' . $_SERVER['HTTP_REFERER'] ?? 'cart.php');
exit;

進階優化與避坑指南

1. Cookie 體積限制應對

  • 精簡字段:僅存儲必要字段,圖片 URL 可存相對路徑或 CDN 短鏈
  • 壓縮序列化:gzcompress(serialize($data)) 存儲,讀取時 unserialize(gzuncompress($data))
  • 分片存儲:超大購物車拆分為 cart_1、cart_2 多個 Cookie
  • 降級方案:檢測 Cookie 寫入失敗時,提示用戶登入並同步至服務端資料庫

2. 安全性加固

// 簽名驗證防篡改
const SECRET = 'your-secret-key';
public function save() {
    $data = serialize($this->cartArray);
    $sig = hash_hmac('sha256', $data, SECRET);
    setcookie("cartapi", $data, ...);
    setcookie("cartapi_sig", $sig, ...);
}
public function cartView() {
    $data = $_COOKIE['cartapi'] ?? '';
    $sig = $_COOKIE['cartapi_sig'] ?? '';
    if (!hash_equals(hash_hmac('sha256', $data, SECRET), $sig)) {
        return false; // 數據被篡改,拋棄
    }
    return @unserialize($data);
}

3. 合併策略優化

當前以 id 為唯一鍵。若需支援「同款不同規格」(如顏色、尺碼),建議鍵值改為 "{$id}:{$spec_signature}",$spec_signature 為規格屬性有序序列化的哈希。

總結

CartApi 類提供了開箱即用的 Cookie 購物車解決方案,代碼約 150 行,無第三方依賴,涵蓋電商核心流程所需的全部操作介面。通過本文的進階優化建議(體積控制、簽名驗證、規格擴展),可平滑支撐日均萬單級別的中小型電商業務。對於大型系統,建議採用「Cookie 暫存 + 登入後同步至 Redis/資料庫」的混合架構,兼顧匿名用戶體驗與數據一致性。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved