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

PHP 身分證字號驗證與轉換工具:15碼升級18碼、檢查碼計算完整實作

     編輯:PHP 相關知識

台灣身分證字號驗證是表單檢核常見需求。本文提供符合國標 GB 11643-1999 的完整 PHP 實作,包含檢查碼計算、15 碼升級 18 碼、格式驗證與年齡推算功能,可直接整合至專案中。

核心演算法:檢查碼計算

身分證字號第 1 碼字母對應數字(A=10, B=11... Z=35),拆成十位與個位,配合加權因子計算總和,對 11 取餘,對照表取得檢查碼。

<?php
class TaiwanIdValidator {
    // 字母對應數字表(A=10 ~ Z=35)
    private static array $letterMap = [
        'A' => 10, 'B' => 11, 'C' => 12, 'D' => 13, 'E' => 14, 'F' => 15,
        'G' => 16, 'H' => 17, 'I' => 34, 'J' => 18, 'K' => 19, 'L' => 20,
        'M' => 21, 'N' => 22, 'O' => 35, 'P' => 23, 'Q' => 24, 'R' => 25,
        'S' => 26, 'T' => 27, 'U' => 28, 'V' => 29, 'W' => 32, 'X' => 30,
        'Y' => 31, 'Z' => 33
    ];

    // 加權因子(含字母拆分後的 10、9)
    private static array $weights = [1, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1];

    // 檢查碼對照表(餘數 0~10 對應)
    private static array $checkCodeMap = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];

    /**
     * 計算檢查碼
     * @param string $idBase 前 17 碼(含字母展開)
     * @return string 檢查碼
     */
    public static function calculateCheckCode(string $idBase): string {
        $sum = 0;
        for ($i = 0; $i < 11; $i++) {
            $sum += (int)$idBase[$i] * self::$weights[$i];
        }
        return self::$checkCodeMap[$sum % 11];
    }

    /**
     * 驗證 18 碼身分證
     * @param string $id 完整身分證字號
     * @return bool
     */
    public static function validate18(string $id): bool {
        if (!preg_match('/^[A-Z][12][0-9]{8}[0-9X]$/i', $id)) {
            return false;
        }
        $id = strtoupper($id);
        $letter = $id[0];
        $numPart = substr($id, 1, 8);
        $checkCode = $id[10];

        // 字母展開
        $letterNum = self::$letterMap[$letter];
        $idBase = ($letterNum / 10) . ($letterNum % 10) . $numPart;

        return self::calculateCheckCode($idBase) === $checkCode;
    }

    /**
     * 15 碼升級 18 碼(舊版身分證)
     * @param string $id15 15 碼身分證
     * @return string|false 18 碼身分證或失敗回傳 false
     */
    public static function upgrade15to18(string $id15): string|false {
        if (!preg_match('/^[A-Z][12][0-9]{13}$/i', $id15)) {
            return false;
        }
        $id15 = strtoupper($id15);
        $letter = $id15[0];
        $gender = $id15[1];
        $birthPart = substr($id15, 2, 6); // YYMMDD
        $serial = substr($id15, 8, 6);
        $checkCode15 = $id15[14];

        // 判斷出生年份世紀(15 碼無世紀資訊,依序號判斷)
        $year = (int)substr($birthPart, 0, 2);
        $fullYear = ($serial >= '996000' && $serial <= '999999') ? 1800 + $year : 1900 + $year;
        $birthDate = sprintf('%04d%s', $fullYear, substr($birthPart, 2));

        // 組合前 17 碼
        $letterNum = self::$letterMap[$letter];
        $idBase = ($letterNum / 10) . ($letterNum % 10) . $gender . $birthDate . $serial;

        // 計算新檢查碼
        $newCheckCode = self::calculateCheckCode($idBase);

        return $letter . $gender . $birthDate . $serial . $newCheckCode;
    }

    /**
     * 從身分證推算生日與性別
     * @param string $id
     * @return array|false ['birth' => 'YYYY-MM-DD', 'gender' => 'M'|'F', 'age' => int]
     */
    public static function parseInfo(string $id): array|false {
        if (strlen($id) === 15) {
            $id = self::upgrade15to18($id);
            if (!$id) return false;
        }
        if (!self::validate18($id)) return false;

        $id = strtoupper($id);
        $genderCode = $id[1];
        $birthStr = substr($id, 2, 8);
        $birth = DateTime::createFromFormat('Ymd', $birthStr);
        if (!$birth) return false;

        $today = new DateTime();
        $age = $today->diff($birth)->y;

        return [
            'birth' => $birth->format('Y-m-d'),
            'gender' => $genderCode === '1' ? 'M' : 'F',
            'age' => $age,
            'area' => self::getAreaName($id[0])
        ];
    }

    private static function getAreaName(string $letter): string {
        $areas = [
            'A' => '台北市', 'B' => '台中市', 'C' => '基隆市', 'D' => '台南市',
            'E' => '高雄市', 'F' => '新北市', 'G' => '宜蘭縣', 'H' => '桃園市',
            'I' => '嘉義市', 'J' => '新竹縣', 'K' => '苗栗縣', 'L' => '台中縣',
            'M' => '南投縣', 'N' => '彰化縣', 'O' => '新竹市', 'P' => '雲林縣',
            'Q' => '嘉義縣', 'R' => '台南縣', 'S' => '高雄縣', 'T' => '屏東縣',
            'U' => '花蓮縣', 'V' => '台東縣', 'W' => '金門縣', 'X' => '澎湖縣',
            'Y' => '陽明山', 'Z' => '連江縣'
        ];
        return $areas[$letter] ?? '未知';
    }
}
?>

使用範例

<?php
require 'TaiwanIdValidator.php';

// 驗證 18 碼
var_dump(TaiwanIdValidator::validate18('A123456789')); // true/false

// 15 碼升級
var_dump(TaiwanIdValidator::upgrade15to18('A12345678901234')); // 'A12345678901234X'

// 解析資訊
$info = TaiwanIdValidator::parseInfo('A123456789');
if ($info) {
    echo "生日:{$info['birth']}\n";
    echo "性別:" . ($info['gender'] === 'M' ? '男' : '女') . "\n";
    echo "年齡:{$info['age']} 歲\n";
    echo "戶籍地:{$info['area']}\n";
}
?>

表單整合範例(Laravel 驗證規則)

<?php
// App/Rules/TaiwanId.php
namespace App\Rules;

use Illuminate\Contracts\Validation\Rule;

class TaiwanId implements Rule {
    public function passes($attribute, $value): bool {
        return TaiwanIdValidator::validate18($value) || TaiwanIdValidator::upgrade15to18($value) !== false;
    }

    public function message(): string {
        return '身分證字號格式錯誤';
    }
}

// 控制器中使用
$request->validate([
    'id_number' => ['required', new TaiwanId()]
]);
?>

效能優化建議

  • 靜態快取字母對照表與加權因子,避免重複建立陣列
  • 大量驗證時可預編譯正則表達式
  • 搭配 Redis 快取常用查詢結果

此實作嚴格遵循國標演算法,支援新舊版身分證相互轉換,並提供生日、性別、年齡、戶籍地等衍生資訊,適合會員系統、實名認證、問卷調查等場景使用。

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