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

CodeIgniter 購物車類中文支援問題:根因分析與三種解決方案

     編輯:PHP 綜合知識

前言

CodeIgniter 3.x 內建的 Cart Library 預設限制商品名稱只能包含英數字、破音符號、底線、冒號與空格,導致中文商品名無法加入購物車。本文深入分析源碼限制邏輯,提供三種解決方案並對比優劣,助你快速修復並理解底層機制。

一、問題根因:源碼分析

檔案:system/libraries/Cart.php (約第 186-195 行)

<?php
// 原始驗證邏輯
if (!preg_match("/^[".$this->product_name_rules."]+$/i", $items['name'])) {
    log_message('error', 'An invalid name was submitted as the product name: '.$items['name'].' The name can only contain alpha-numeric characters, dashes, underscores, colons, and spaces');
    return FALSE;
}

// 預設規則定義 (約第 45 行)
public $product_name_rules = 'a-z0-9:\-\_\ '; // 只允許英數、冒號、連字符、底線、空格

核心問題:正則模式 a-z0-9:\-\_\ 不包含任何 Unicode 字元範圍,中文、日文、韓文、表情符號等均會被拒絕。

二、方案一:修改正則規則 (最快、最直接)

1. 允許所有可見字元 (含中文)

<?php
// 在 Controller 建構子或 MY_Cart 擴展中設置
$this->cart->product_name_rules = 'a-z0-9:\-\_\ \p{L}\p{N}\p{P}\p{S}'; 
// \p{L} 任意語言字母, \p{N} 數字, \p{P} 標點, \p{S} 符號 (含表情)

優點:一行配置解決,無需修改核心檔。
缺點:過於寬鬆,可能允許惡意字元 (如控制字元、零寬空格),需配合前端/其他層驗證。

2. 精確允許中日韓統一表意文字 (CJK)

<?php
$this->cart->product_name_rules = 'a-z0-9:\-\_\ \x{4e00}-\x{9fff}\x{3400}-\x{4dbf}\x{20000}-\x{2a6df}\x{2a700}-\x{2b73f}\x{2b740}-\x{2b81f}\x{2b820}-\x{2ceaf}\xf900-\x{faff}\x{3300}-\x{33ff}\x{fe30}-\x{fe4f}\x{f900}-\x{faff}';
// 涵蓋基本區、擴展A/B/C/D/E/F、相容字元、康熙部首等

優點:精確控制允許字元集。
缺點:正則極長難維護,漏字風險高,不支援 PHP 7.3 以下版本的 Unicode 屬性逃逸。

三、方案二:擴展 MY_Cart 類 (推薦、符合框架規範)

建立 application/libraries/MY_Cart.php 繼承核心類,重寫驗證邏輯:

<?php
defined('BASEPATH') OR exit('No direct script access allowed');

class MY_Cart extends CI_Cart {
    /**
     * 重寫 name 驗證:允許 UTF-8 可見字元,拒絕控制字元
     */
    protected function _validate_name(string $name): bool {
        // 1. 長度限制 (防止異常長名稱撐爆 session/cookie)
        if (mb_strlen($name, 'UTF-8') > 255) {
            log_message('error', 'Cart name too long: ' . mb_strlen($name));
            return false;
        }
        
        // 2. 字元白名單:字母、數字、常用標點、空格、CJK 統一表意文字
        // 使用 u 修飾符啟用 UTF-8 模式
        $pattern = '/^[\p{L}\p{N}\p{P}\p{Zs}\p{Sc}\p{Sk}\p{Sm}\p{So}]+$/u';
        // \p{L} 字母, \p{N} 數字, \p{P} 標點, \p{Zs} 空格分隔符, \p{Sc} 貨幣符號, \p{Sk} 修飾符號, \p{Sm} 數學符號, \p{So} 其他符號
        
        if (!preg_match($pattern, $name)) {
            // 記錄被拒絕的字元 (調試用)
            $invalidChars = preg_replace($pattern, '', $name);
            log_message('debug', 'Cart rejected name: ' . $name . ' | invalid chars: ' . bin2hex($invalidChars));
            return false;
        }
        
        // 3. 拒絕控制字元、格式化字元、代理區 (防 XSS/注入)
        if (preg_match('/[\p{Cc}\p{Cf}\p{Cs}]/u', $name)) {
            log_message('error', 'Cart name contains control/format/surrogate chars: ' . $name);
            return false;
        }
        
        return true;
    }

    /** 覆寫 insert 方法調用新驗證 */
    public function insert($items = []) {
        // ... 複製父類 insert 邏輯,將原驗證替換為 $this->_validate_name($items['name'])
        // 為簡潔起見此處省略完整複製,實際請從 CI_Cart 複製 insert() 並修改驗證行
    }
}

優點:

  • 不修改核心檔,升級框架無衝突。
  • 集中管理驗證邏輯,易於單元測試。
  • 可擴展:添加 XSS 過濾、敏感詞過濾、長度自定義等。

注意:CodeIgniter 3.x 載入 Library 時會自動尋找 MY_ 前綴類,無需額外配置。

四、方案三:預處理商品名 (不改動驗證、相容性最好)

在加入購物車前,將中文名編碼為安全字串 (Base64 / JSON / URL 編碼),取出時解碼:

<?php
// 加入購物車前
$safeName = base64_encode($chineseName); // 或 json_encode($chineseName)
$this->cart->insert([
    'id'      => $productId,
    'qty'     => 1,
    'price'   => $price,
    'name'    => $safeName, // 現在只包含 A-Z a-z 0-9 + / =
    'options' => ['original_name' => $chineseName] // 可選:原名存 options
]);

// 讀取購物車時
foreach ($this->cart->contents() as $item) {
    $displayName = base64_decode($item['name']);
    // 或從 options 取原名
    echo $displayName;
}

優點:零修改框架代碼,相容所有 CI 版本。
缺點:

  • 購物車 Session/資料庫中存儲的是編碼後字串,難以直接查詢/報表/搜尋。
  • 若用 base64 產生 + / = 可能觸發其他驗證 (需確認 product_name_rules 包含這些)。
  • 增加編解碼開銷 (微乎其微)。

五、方案對比與選型建議

維度方案一: 改規則方案二: MY_Cart方案三: 預編碼
實施速度⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
安全性⭐⭐ (需自行加強)⭐⭐⭐⭐⭐⭐⭐⭐⭐
可維護性⭐⭐ (分散在配置中)⭐⭐⭐⭐⭐ (集中封裝)⭐⭐⭐ (業務代碼耦合)
升級相容⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
搜尋/報表友好⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐ (需解碼)
推薦場景快速原型、內部工具正式專案、長期維護極度不想動框架、臨時方案

六、完整驗證正則參考 (生產環境建議)

<?php
// 允許:各語言文字、數字、常用標點、空格、貨幣符號、基本表情符號
// 拒絕:控制字元(Cc)、格式化字元(Cf)、代理對(Cs)、私有使用區(Co)
const CART_NAME_PATTERN = '/^
    [\p{L}\p{N}\p{P}\p{Zs}\p{Sc}\p{Sk}\p{Sm}\p{So}]  # 允許字元類別
    +                                                 # 至少一個
    $/ux';                                            // u: UTF-8, x: 忽略空白註釋

// 使用範例
function isValidCartName(string $name): bool {
    if (mb_strlen($name) > 200) return false;
    if (!preg_match(self::CART_NAME_PATTERN, $name)) return false;
    if (preg_match('/[\p{Cc}\p{Cf}\p{Cs}\p{Co}]/u', $name)) return false;
    return true;
}

七、CodeIgniter 4.x 差異說明

CI 4 的 CodeIgniter\Cart\Cart 使用 Validation 服務驗證,規則定義在 Config\Validation.php 或模型 $validationRules 中,預設規則為 alpha_numeric_punct (含基本標點)。修改方式:

<?php
// app/Config/Validation.php
public $cartItem = [
    'name' => 'required|max_length[255]|utf8|regex_match[/^[\p{L}\p{N}\p{P}\p{Zs}\p{Sc}\p{Sk}\p{Sm}\p{So}]+$/u]',
];

CI 4 內建 utf8 規則已檢查有效 UTF-8 編碼,配合 regex_match 更安全簡潔。

結語

CodeIgniter 購物車中文問題本質是舊版正則未考慮 Unicode。生產專案強烈建議採用 方案二 (MY_Cart 擴展),集中封裝驗證邏輯,兼顧安全性、可維護性與升級相容性。若專案已遷移至 CI 4,請直接調整 Validation 規則,享受現代化驗證體系帶來的便利。

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