這是一個經典的信用卡驗證函式庫(版本 3.5,2000 年發布),採用四步驟驗證流程,支援 8 種主流卡別。雖年代久遠,但其邏輯架構清晰,適合學習驗證流程設計。
<?php
class CreditCardValidator {
private string $cardName = '';
public function validate(string $number): bool {
$number = $this->onlyNumeric($number);
// Step 2: 前綴辨識
if (!$this->identifyCardType($number)) return false;
// Step 3: 長度驗證
if (!$this->checkLength($number)) return false;
// Step 4: Luhn 校驗
return $this->mod10Check($number);
}
public function getCardName(): string {
return $this->cardName;
}
private function onlyNumeric(string $number): string {
return substr(preg_replace('/\D/', '', $number), 0, 19);
}
private function identifyCardType(string $number): bool {
$prefix4 = (int)substr($number, 0, 4);
$len = strlen($number);
$rules = [
// [minPrefix, maxPrefix, cardName, expectedLength]
[3000, 3059, 'Diners Club', 14],
[3600, 3699, 'Diners Club', 14],
[3800, 3889, 'Diners Club', 14],
[3400, 3499, 'American Express', 15],
[3700, 3799, 'American Express', 15],
[3528, 3589, 'JCB', 16],
[3890, 3899, 'Carte Blanche', 14],
[4000, 4999, 'Visa', null], // Visa 長度特殊處理
[5100, 5599, 'MasterCard', 16],
[5610, 5610, 'Australian BankCard', 16],
[6011, 6011, 'Discover/Novus', 16],
];
foreach ($rules as [$min, $max, $name, $expectedLen]) {
if ($prefix4 >= $min && $prefix4 <= $max) {
$this->cardName = $name;
if ($name === 'Visa') {
$this->expectedLength = $len > 14 ? 16 : ($len < 14 ? 13 : -1);
} else {
$this->expectedLength = $expectedLen;
}
return true;
}
}
return false;
}
private int $expectedLength = 0;
private function checkLength(string $number): bool {
$len = strlen($number);
if ($this->expectedLength === -1) return false; // Visa 14 碼無效
if ($len !== $this->expectedLength) {
$diff = $len - $this->expectedLength;
// 可在這裡記錄錯誤訊息
return false;
}
return true;
}
private function mod10Check(string $number): bool {
$sum = 0;
$len = strlen($number);
$parity = $len % 2;
for ($i = 0; $i < $len; $i++) {
$digit = (int)$number[$i];
if ($i % 2 === $parity) {
$digit *= 2;
if ($digit > 9) $digit -= 9;
}
$sum += $digit;
}
return $sum % 10 === 0;
}
}
// 使用範例
$validator = new CreditCardValidator();
$cards = [
'4532 0151 1283 0366', // Visa
'5555 5555 5555 4444', // MasterCard
'3782 822463 10005', // Amex
'6011 1111 1111 1117', // Discover
'1234 5678 9012 3456', // Invalid
];
foreach ($cards as $card) {
$valid = $validator->validate($card);
echo "卡號:$card => " . ($valid ? "✅ {$validator->getCardName()}" : "❌ 無效") . "\n";
}
?>
| 特性 | 原始版 (2000) | 現代重構 |
|---|---|---|
| 架構 | 全域函數 + 全域變數 | 類別封裝、單一職責 |
| 錯誤處理 | 直接 echo HTML | 回傳布林/拋出例外 |
| 卡別範圍 | 2000 年資料 | 可擴充陣列配置 |
| Visa 14 碼處理 | 直接輸出錯誤 HTML | 回傳 false 由呼叫端處理 |
| 測試性 | 難以單元測試 | 易於 Mock 與測試 |
// 在 $rules 陣列新增 [6200, 6299, 'UnionPay', 16], // UnionPay 62 開頭 [5018, 5018, 'Maestro', 12], // Maestro 部分範圍 [5020, 5020, 'Maestro', 12], [5038, 5038, 'Maestro', 12], [6304, 6304, 'Maestro', 16], [6759, 6759, 'Maestro', 16], [6761, 6763, 'Maestro', 16],
經典演算法邏輯不變,但程式架構需與時俱進。現代專案建議: