PHP 实现轻量级购物车类:仿 CodeIgniter 核心逻辑

何川
何川
发布时间:2026-10-14 | 2人浏览过
原创

本文详细解析基于 PHP 的手写购物车类实现,重点讲解如何利用数组结构结合 Session 进行状态存储,以及如何验证输入参数并自动计算购物车总额。

购物车核心功能与设计目标

在 Web 应用中,购物车是连接用户浏览与支付的关键模块。为了实现轻量且高效的购物车逻辑,本示例参考了 CodeIgniter 框架的设计思路,主要解决以下问题:

  1. 商品管理:支持单个或多个商品加入购物车,以及根据 RowID 调整或删除商品。
  2. 数据校验:对商品 ID、名称、价格和数量进行严格过滤,防止非法字符注入。
  3. 自动统计:实时更新购物车内的商品种类、总数量和总金额(Subtotal 与 Total)。
  4. 状态保持:利用 $_SESSION 跨页面保持购物车状态。

第1步:构建 Cart 类基础结构

首先定义购物车类的核心属性。我们需要一个私有数组 $_cart_contents 来暂存当前会话的购物车数据,并配置正则表达式规则以确保存入的数据安全。

  • product_id_rule:限制商品 ID 仅包含小写字母、数字及特殊符号 ._-。
  • product_name_rule:限制商品名称允许额外包含冒号 :。
  • _cart_contents:核心存储数组,初始化为包含 cart_total 和 total_items 的结构体。

第2步:实现商品插入逻辑 (insert)

插入功能是购物车的基础。该方法接受一维或多维数组输入,内部调用私有方法 _insert 处理单个商品。

  1. 参数校验:确保输入为有效数组且包含必填字段 id, qty, price, name。
  2. 数据清洗:
    - 移除数量中的非数字字符及前导零。
    - 移除价格中的非数字/小数点字符。
    - 使用正则表达式验证 ID 和 Name 是否符合预设规则。
  3. 生成唯一标识 (RowID):
    - 若商品存在 options(如颜色、尺寸),则 RowID = md5(id + options拼接)。
    - 若无选项,则 RowID = md5(id)。
    这确保了同一商品的不同规格在购物车中被视为独立项。
  4. 保存至数组:将处理后的数据存入 $_cart_contents 并以 rowid 为键。

第3步:实现购物车更新逻辑 (update)

当用户调整数量时,需调用 update 方法。该方法依赖商品唯一的 rowid 和目标新数量。

  1. 查找商品:根据传入的 rowid 在 $_cart_contents 中定位对应商品。
  2. 数量处理:
    - 同样进行数字清洗。
    - 若新数量为 0,则从购物车中 unset 该商品(相当于删除)。
    - 若数量大于 0 且与原数量不同,则更新数量字段。

第4步:自动统计与 Session 持久化

每次增删改操作后,必须重新计算统计数据并刷新 Session,以保证界面显示准确。

  1. 清除旧统计:重置 total_items 和 cart_total。
  2. 遍历计算:
    - 遍历购物车数组,累加 price * qty 得到总金额。
    - 同时为每个商品计算 subtotal。
  3. 更新总量:
    - total_items 等于数组中实际商品键值的数量(排除统计字段)。
    - cart_total 为累加的总金额。
  4. 保存 Session:将整理好的 $_cart_contents 写入 $_SESSION['cart_contents']。若购物车为空(仅剩统计字段),则清空 Session 以释放资源。

第5步:其他辅助方法

  • destroy():清空购物车数组并销毁 Session 中的购物车数据。
  • contents() / total() / total_items():提供只读接口供视图层获取购物车详情。
  • has_options():检查特定商品是否存在自定义属性。

完整代码实现

以下是完整的 Cart.php 类实现,可直接集成到项目中。

<?php
class Cart {
 private $product_id_rule = '\.a-z0-9-_';
 private $product_name_rule = '\.\:a-z0-9-_';
 private $debug = TRUE;
 private $_cart_contents = array();

 public function __construct() {
  if(isset($_SESSION['cart_contents'])) {
   $this->_cart_contents = $_SESSION['cart_contents'];
  } else {
   $this->_cart_contents['cart_total'] = 0;
   $this->_cart_contents['total_items'] = 0;
  }
 }

 public function insert($items = array()) {
  if( ! is_array($items) OR count($items) == 0) return FALSE;
  
  $save_cart = FALSE;
  if(isset($items['id'])) {
   if($this->_insert($items) === TRUE) $save_cart = TRUE;
  } else {
   foreach($items as $val) {
    if(is_array($val) AND isset($val['id'])) {
     if($this->_insert($val) == TRUE) $save_cart = TRUE;
    }
   }
  }
  if($save_cart) {
   $this->_save_cart();
   return TRUE;
  }
  return FALSE;
 }

 public function update($items = array()) {
  if( !is_array($items) OR count($items) == 0) return FALSE;
  
  $save_cart = FALSE;
  if(isset($items['rowid']) AND isset($items['qty'])) {
   if($this->_update($items) === TRUE) $save_cart = TRUE;
  } else {
   foreach($items as $val) {
    if(is_array($val) AND isset($val['rowid']) AND isset($val['qty'])) {
     if($this->_update($val) === TRUE) $save_cart = TRUE;
    }
   }
  }
  if($save_cart) {
   $this->_save_cart();
   return TRUE;
  }
  return FALSE;
 }

 public function total() {
  return $this->_cart_contents['cart_total'];
 }

 public function total_items() {
  return $this->_cart_contents['total_items'];
 }

 public function contents() {
  return $this->_cart_contents;
 }

 public function destroy() {
  unset($this->_cart_contents);
  $this->_cart_contents['cart_total'] = 0;
  $this->_cart_contents['total_items'] = 0;
  unset($_SESSION['cart_contents']);
 }

 private function has_options($rowid = '') {
  if( ! isset($this->_cart_contents[$rowid]['options']) OR count($this->_cart_contents[$rowid]['options']) === 0) {
   return FALSE;
  }
  return TRUE;
 }

 private function _insert($items = array()) {
  if( ! is_array($items) OR count($items) == 0) return FALSE;
  if( ! isset($items['id']) OR ! isset($items['qty']) OR ! isset($items['price']) OR ! isset($items['name'])) return FALSE;

  // Sanitize Qty
  $items['qty'] = trim(preg_replace('/([^0-9])/i', '', $items['qty']));
  $items['qty'] = trim(preg_replace('/^([0]+)/i', '', $items['qty']));
  if( ! is_numeric($items['qty']) OR $items['qty'] == 0) return FALSE;

  // Validate ID
  if( ! preg_match('/^['.$this->product_id_rule.']+$/i', $items['id'])) return FALSE;
  // Validate Name
  if( ! preg_match('/^['.$this->product_name_rule.']+$/i', $items['name'])) return FALSE;

  // Sanitize Price
  $items['price'] = trim(preg_replace('/([^0-9\.])/i', '', $items['price']));
  $items['price'] = trim(preg_replace('/^([0]+)/i', '', $items['price']));
  if( ! is_numeric($items['price'])) return FALSE;

  // Generate RowID
  if(isset($items['options']) AND count($items['options']) >0) {
   $rowid = md5($items['id'].implode('', $items['options']));
  } else {
   $rowid = md5($items['id']);
  }

  // Add to cart
  unset($this->_cart_contents[$rowid]);
  $this->_cart_contents[$rowid]['rowid'] = $rowid;
  foreach($items as $key => $val) {
   $this->_cart_contents[$rowid][$key] = $val;
  }
  return TRUE;
 }

 private function _update($items = array()) {
  if( ! isset($items['rowid']) OR ! isset($items['qty']) OR ! isset($this->_cart_contents[$items['rowid']])) return FALSE;

  $items['qty'] = preg_replace('/([^0-9])/i', '', $items['qty']);
  $items['qty'] = preg_replace('/^([0]+)/i', '', $items['qty']);
  if( ! is_numeric($items['qty'])) return FALSE;

  if($this->_cart_contents[$items['rowid']]['qty'] == $items['qty']) return FALSE;

  if($items['qty'] == 0) {
   unset($this->_cart_contents[$items['rowid']]);
  } else {
   $this->_cart_contents[$items['rowid']]['qty'] = $items['qty'];
  }
  return TRUE;
 }

 private function _save_cart() {
  unset($this->_cart_contents['total_items']);
  unset($this->_cart_contents['cart_total']);

  $total = 0;
  foreach($this->_cart_contents as $key => $val) {
   if( ! is_array($val) OR ! isset($val['price']) OR ! isset($val['qty'])) continue;
   $total += ($val['price'] * $val['qty']);
   $this->_cart_contents[$key]['subtotal'] = ($val['price'] * $val['qty']);
  }

  $this->_cart_contents['total_items'] = count($this->_cart_contents);
  $this->_cart_contents['cart_total'] = $total;

  if(count($this->_cart_contents) <= 2) {
   unset($_SESSION['cart_contents']);
   return FALSE;
  }

  $_SESSION['cart_contents'] = $this->_cart_contents;
  return TRUE;
 }
}
?>

使用示例与测试

以下代码展示了如何实例化该类,插入带有可选属性的商品,并更新其数量。

<?php
session_start();
require_once('cart.php');

$items = array(
   0 => array(
   'id' => 'sp001',
   'qty' => 20,
   'price' => '10.50',
   'name' => 'Product A',
   'options' => array(
       'color' => 'red',
       'size' => 'L'
       )
   ),
   1 => array(
   'id' => 'sp002',
   'qty' => 1,
   'price' => '3.50',
   'name' => 'Product B'
   )
  );

$arr = array(
   'rowid' => '生成的具体RowID值',
   'qty' => 21
  );

$cart = new Cart();
$cart->insert($items);
// $cart->update($arr); // 取消注释以测试更新
var_dump($cart->contents());
?>

通过以上步骤,您可以获得一个具备基本电商功能的轻量级购物车组件。该实现不依赖任何外部框架,易于移植和维护。以上就是 PHP 手写购物车类的详细内容,更多关于 PHP 面向对象编程的资料请关注本站其它相关文章!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表