校验身份证号是否合法(包含港澳台)

前言:

一代身份证与二代身份证的区别

一代身份证是15位,二代身份证是18位;
一代身份证出生年月日采用YYMMDD格式,二代身份证出生年月日采用YYYYMMDD格式;
一代身份证无校验码,二代身份证有校验码。

二代身份证号码的编码规则

身份证号码前六位:表示编码对象常住户口所在县(市、镇、区)的行政区划代码。1-2位省、自治区、直辖市代码; 3-4位地级市、盟、自治州代码; 5-6位县、县级市、区代码;
身份证号码第七位到第十四位:表示编码对象出生的年、月、日,其中年份用四位数字表示年、月、日之间不用分隔符,采用YYYYMMDD格式;
身份证号码第十五位到十七位:地址码所标识的区域范围内,对同年、月、日出生的人员编定的顺序号。顺序码的奇数分配给男性,偶数分配给女性,即第17位奇数表示男性,偶数表示女性;
身份证号码最后一位:根据前面十七位数字码,按照ISO 7064:1983.MOD 11-2校验码计算出来的检验码。如果某人的尾号是0-9,都不会出现X,但如果尾号是10,那么就得用X来代替,X是罗马数字的10,用X来代替10,可以保证公民的身份证符合国家标准;

二代身份证校验规则(最后一位校验码的计算方法)

首先将前面的身份证号码17位数分别乘以不同的系数。从第一位到第十七位的系数分别为:7-9-10-5-8-4-2-1-6-3-7-9-10-5-8-4-2
接下来将这17位数字和系数相乘的结果相加,然后用加出来和除以11,看余数,余数只可能有0-1-2-3-4-5-6-7-8-9-10这11个数字。其分别对应的最后一位身份证的号码为1-0-X -9-8-7-6-5-4-3-2

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
/**
* User: 謝雲虎
* DateTime: 2021/11/4 17:45
* Desc:判断是否为合法的身份证号码
* @param string $vStr
* @return bool
*/

/**
* 校验身份证号是否合法
* @param string $num 待校验的身份证号
* @return bool
*/
function isValid($num)
{
//老身份证长度15位,新身份证长度18位
$length = strlen($num);
if ($length == 15) { //如果是15位身份证
//15位身份证没有字母
if (!is_numeric($num)) {
return false;
}
// 省市县(6位)
$areaNum = substr($num, 0, 6);
// 出生年月(6位)
$dateNum = substr($num, 6, 6);

} else if ($length == 18) { //如果是18位身份证

//基本格式校验
if (!preg_match('/^\d{17}[0-9xX]$/', $num)) {
return false;
}
// 省市县(6位)
$areaNum = substr($num, 0, 6);
// 出生年月日(8位)
$dateNum = substr($num, 6, 8);

} else { //假身份证
return false;
}
//验证地区
if (!isAreaCodeValid($areaNum)) {
return false;
}
//验证日期
if (!isDateValid($dateNum)) {
return false;
}
//验证最后一位
if (!isVerifyCodeValid($num)) {
return false;
}
return true;
}

/**
* 省市自治区校验
* @param string $area 省、直辖市代码
* @return bool
*/
function isAreaCodeValid($area) {
$provinceCode = substr($area, 0, 2);
// 根据GB/T2260—999,省市代码11到65
if (11 <= $provinceCode && $provinceCode <= 65) {
return true;
} else {
return false;
}
}

/**
* 验证出生日期合法性
* @param string $date 日期
* @return bool
*/
function isDateValid( $date) {
if (strlen($date) == 6) { //15位身份证号没有年份,这里拼上年份
$date = '19'.$date;
}
$year = intval(substr($date, 0, 4));
$month = intval(substr($date, 4, 2));
$day = intval(substr($date, 6, 2));

//日期基本格式校验
if (!checkdate($month, $day, $year)) {
return false;
}

//日期格式正确,但是逻辑存在问题(如:年份大于当前年)
$currYear = date('Y');
if ($year > $currYear) {
return false;
}
return true;
}

/**
* 验证18位身份证最后一位
* @param string $num 待校验的身份证号
* @return bool
*/
function isVerifyCodeValid($num)
{
if (strlen($num) == 18) {
$factor = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
$tokens = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];
$checkSum = 0;
for ($i = 0; $i < 17; $i++) {
$checkSum += intval($num{$i}) * $factor[$i];
}
$mod = $checkSum % 11;
$token = $tokens[$mod];

$lastChar = strtoupper($num{17});

if ($lastChar != $token) {
return false;
}
}
return true;
}

/**
* User: 謝雲虎
* DateTime: 2021/11/4 18:02
* Desc:简单验证港澳台身份证
* @param $idcard
* @return bool
*/
function idcardCheckOther($idcard){
$idcard = strtoupper($idcard);
$idcard = str_replace(array('(',')'), array('(',')'), $idcard);
preg_match('/^([A-Z])([0-9]{6})\(([A0-9]{1})\)$/', $idcard,$match);//香港
if ($match && count($match)==4)
{
$sum = (ord($match[1])-64)*8;
$index = 7;
for($j=0;$j<6;$j++)
{
$sum += $match[2]{$j}*$index;
$index--;
}
$get_num = $sum%11;
if ($get_num==1) $get_num = 'A';
elseif ($get_num>1) $get_num = 11-$get_num;

if ($match[3]==$get_num) return true;
return false;
}
preg_match('/^([A-Z])([0-9]{9})$/', $idcard,$taiwan);//中国台湾省

if ($taiwan && count($taiwan)==3)//首位数字代表性别,男性为1、女性为2
{
$area_code = array('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);
$code = $area_code[$taiwan[1]];
$sum = $code{0} + $code{1}*9;
$index = 8;
for($k=1;$k<8;$k++)
{
$sum += $taiwan[2]{$k} * $index;
$index--;
}
$get_num = $sum%10;
if ($get_num==$taiwan[2]{8}) return true;
return true;
}
preg_match('/^[157][0-9]{6}\([0-9]\)$/', $idcard,$aomen);//澳门
if ($aomen)
{
return true;
}
return false;
}