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

PHP 實現 Luhn 算法(Mod-10)驗證信用卡號:原理、代碼與邊界測試

     編輯:PHP 相關知識

算法背景

Luhn 算法(又稱 Mod-10)是 ISO/IEC 7812-1 標準,廣泛用於信用卡號、IMEI、加拿大社會保險號等識別碼的校驗位驗證。它能檢測單位數字錯誤、相鄰數字交換等常見輸入錯誤,但非加密算法,不可用於安全防護。

算法步驟

  1. 從右向左(不含校驗位)或從左向右(含校驗位)遍歷數字。
  2. 每隔一位數字乘以 2(即偶數位置),若積 ≥ 10,則減 9(等價於十位+個位)。
  3. 所有處理後的數字求和。
  4. 總和模 10 為 0 則校驗通過。

PHP 實現(含詳細註釋)

<?php
/**
 * Luhn 算法驗證
 * @param string $number 僅包含數字的字串
 * @return bool true=校驗通過
 */
function luhnCheck(string $number): bool
{
    // 1. 基礎檢查
    if (!ctype_digit($number)) {
        return false; // 含非數字字元
    }
    $len = strlen($number);
    if ($len < 2) {
        return false; // 至少需 1 位數字 + 1 位校驗位
    }

    $sum = 0;
    $double = false; // 從右向左,校驗位不加倍

    // 2. 從右向左遍歷
    for ($i = $len - 1; $i >= 0; $i--) {
        $digit = (int)$number[$i];

        if ($double) {
            $digit *= 2;
            if ($digit > 9) {
                $digit -= 9; // 等價於 $digit = $digit - 9 或 $digit = floor($digit/10) + $digit%10
            }
        }

        $sum += $digit;
        $double = !$double; // 翻轉標誌
    }

    // 3. 模 10 檢查
    return ($sum % 10) === 0;
}

/** 生成校驗位(給定前綴) */
function luhnGenerateCheckDigit(string $prefix): int
{
    // 在末尾加 0 計算總和
    $sum = 0;
    $double = true; // 因為會加上校驗位,所以從右數第二位開始加倍
    for ($i = strlen($prefix) - 1; $i >= 0; $i--) {
        $digit = (int)$prefix[$i];
        if ($double) {
            $digit *= 2;
            if ($digit > 9) $digit -= 9;
        }
        $sum += $digit;
        $double = !$double;
    }
    $checkDigit = (10 - ($sum % 10)) % 10;
    return $checkDigit;
}

測試用例

<?php
$testCases = [
    // 有效卡號(來自維基百科/標準測試向量)
    '4532015112830366' => true,  // Visa
    '5555555555554444' => true,  // Mastercard
    '378282246310005'  => true,  // Amex
    '6011111111111117' => true,  // Discover
    '3566002020360505' => true,  // JCB

    // 無效:修改最後一位
    '4532015112830367' => false,
    // 無效:非數字
    '4532-0151-1283-0366' => false,
    // 無效:長度不足
    '123' => false,
];

foreach ($testCases as $num => $expected) {
    $result = luhnCheck($num);
    $status = $result === $expected ? '✅' : '❌';
    echo "$status [$num] => " . ($result ? 'Valid' : 'Invalid') . " (期望: " . ($expected ? 'Valid' : 'Invalid') . ")\n";
}

// 生成校驗位演示
$prefix = '453201511283036'; // 缺最後一位
$checkDigit = luhnGenerateCheckDigit($prefix);
echo "前綴: $prefix -> 校驗位: $checkDigit -> 完整卡號: {$prefix}{$checkDigit}\n";
echo "驗證完整卡號: " . (luhnCheck("{$prefix}{$checkDigit}") ? '通過' : '失敗') . "\n";

前端整合建議

雖然 PHP 後端必須驗證,但前端即時反饋能大幅提升 UX:

  • 輸入時自動格式化(每 4 位加空格)。
  • 失焦或輸入完畢時調用 JS 版 Luhn 檢查,顯示綠/紅提示。
  • 提交前再次驗證,攔截明顯錯誤,減少後端請求。
// JavaScript 版(ES6)
const luhnCheck = (num) => {
  const digits = num.replace(/\D/g, '').split('').map(Number);
  if (digits.length < 2) return false;
  let sum = 0, double = false;
  for (let i = digits.length - 1; i >= 0; i--) {
    let d = digits[i];
    if (double) { d *= 2; if (d > 9) d -= 9; }
    sum += d;
    double = !double;
  }
  return sum % 10 === 0;
};

常見誤區

誤區正確認知
Luhn 能防止偽造卡號 只能檢測隨機輸入錯誤,攻擊者極易構造合法 Luhn 卡號(只需調整最後一位)。
所有 16 位數字卡號都適用 不同卡組織長度不同:Visa/MC 16 位,Amex 15 位,銀聯 16-19 位。驗證前應先識別 BIN 段。
可用於密碼強度檢查 完全無關,Luhn 僅針對數字串校驗位。

總結

Luhn 算法實現簡單、零依賴,是表單驗證、卡號掃描、設備識別碼校驗的首選。記住:後端必驗、前端輔助、不可信任、非加密。將 luhnCheck 封裝為獨立 Composer 包或工具類,便於多專案複用與單元測試覆蓋。

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