身分證字號規則速覽
- 15 位:
6 位地址碼 + 6 位出生日期(YYMMDD) + 3 位順序碼
- 18 位:
6 位地址碼 + 8 位出生日期(YYYYMMDD) + 3 位順序碼 + 1 位校驗碼
- 校驗碼依 GB 11643-1999 算法:加權求和取模 11 對應字元
完整驗證類別(無正則、無外部依賴)
<?php
class IDCard {
// 加權因子(前 17 位)
private static $factor = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
// 校驗碼對應表(模 11 餘數對應字元)
private static $verifyMap = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];
/**
* 公開靜態方法:驗證身分證字號
* @param string $card 15 或 18 位字串
* @return bool
*/
public static function isCard($card) {
$card = self::to18Card($card);
if (!$card || strlen($card) !== 18) return false;
$base = substr($card, 0, 17);
$check = strtoupper(substr($card, 17, 1));
return self::calcVerify($base) === $check;
}
/**
* 將 15 位升級為 18 位(補齊世紀、計算校驗碼)
*/
public static function to18Card($card) {
$card = trim($card);
if (strlen($card) === 18) return $card;
if (strlen($card) !== 15) return false;
// 百歲以上老人特殊順序碼 996-999,出生年份為 18xx
$seq = substr($card, 12, 3);
$century = in_array($seq, ['996','997','998','999']) ? '18' : '19';
$card18 = substr($card, 0, 6) . $century . substr($card, 6);
$card18 .= self::calcVerify($card18);
return $card18;
}
/**
* 計算校驗碼
*/
private static function calcVerify($base17) {
if (strlen($base17) !== 17) return false;
$sum = 0;
for ($i = 0; $i < 17; $i++) {
$sum += intval($base17[$i]) * self::$factor[$i];
}
return self::$verifyMap[$sum % 11];
}
}
// 使用範例
$id = new IDCard();
var_dump($id::isCard('110105199003078531')); // true
var_dump($id::isCard('110105900307853')); // true (15 位自動升級)
var_dump($id::isCard('123456789012345678')); // false
演算法細節說明
- 地址碼:前 6 位為行政區劃代碼,此處不驗證資料庫,僅做長度檢查。
- 出生日期:18 位第 7-14 碼為 YYYYMMDD,可額外用
checkdate() 驗證合法性。
- 順序碼:第 15-17 碼,奇數通常分給男性、偶數分給女性(非強制)。
- 校驗碼:第 18 碼,0-9 或 X,計算方式為
Σ(數字×加權因子) mod 11 對應映射表。
擴充建議
- 整合行政區劃代碼資料表,驗證地址碼有效性。
- 加入出生日期合法性檢查(
checkdate($m, $d, $y))。
- 支援港澳台居民居住證、護照等其他證件格式。