-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathIDCardFilter.php
More file actions
147 lines (133 loc) · 4.62 KB
/
IDCardFilter.php
File metadata and controls
147 lines (133 loc) · 4.62 KB
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
<?php
/**
** Class IDCardFilter
** 身份证号规则
** 15位: 省份(2位) + 地级市(2位) + 县级市(2位) + 出生年(2位) + 出生月(2位) + 出生日(2位) + 顺序号(3位)
** 18位: 省份(2位) + 地级市(2位) + 县级市(2位) + 出生年(4位) + 出生月(2位) + 出生日(2位) + 顺序号(3位) + 校验位(1位)
** 相比之下,18位 比 15位 多出生年 2位、校验位 1位。
** 其中,顺序号如果是偶数,则说明是女生,顺序号是奇数,则说明是男生。
*/
class IDCardFilter
{
/*
* 校验身份证号码正则
*/
const REGX = '#(^\d{15}$)|(^\d{17}(\d|X)$)#';
/**
** 检测是否是身份证号码
** @param string $idCard
** @return boolean
*/
public function isCardNumber($idCard)
{
return preg_match(self::REGX, $idCard);
}
/**
** 15位转18位
** @param string $idCard
** @return void
*/
public function fifteen2Eighteen($idCard)
{
if (strlen($idCard) != 15) {
return $idCard;
}
// 如果身份证顺序码是996 997 998 999,这些是为百岁以上老人的特殊编码
$code = array_search(substr($idCard, 12, 3), [996, 997, 998, 999]) !== false ? '18' : '19';
$idCardBase = substr($idCard, 0, 6) . $code . substr($idCard, 6, 9);
return $idCardBase . $this->genCode($idCardBase);
}
/**
** 生成校验码
** 有17位数字,分别是:7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2
** 分别用身份证的前 17 位乘以上面相应位置的数字,然后相加。
** 接着用相加的和对 11 取模。
** 用获得的值在下面 11 个字符里查找对应位置的字符,这个字符就是校验位。
** '1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'
** @param string $idCardBase
** @return void
*/
final protected function genCode($idCardBase)
{
$idCardLength = strlen($idCardBase);
if ($idCardLength != 17) {
return false;
}
$factor = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
$verifyNumbers = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];
$sum = 0;
for ($i = 0; $i < $idCardLength; $i++) {
$sum += substr($idCardBase, $i, 1) * $factor[$i];
}
$index = $sum % 11;
return $verifyNumbers[$index];
}
protected $provinces = [
11 => "北京", 12 => "天津", 13 => "河北", 14 => "山西", 15 => "内蒙古",
21 => "辽宁", 22 => "吉林", 23 => "黑龙江", 31 => "上海", 32 => "江苏",
33 => "浙江", 34 => "安徽", 35 => "福建", 36 => "江西", 37 => "山东", 41 => "河南",
42 => "湖北", 43 => "湖南", 44 => "广东", 45 => "广西", 46 => "海南", 50 => "重庆",
51 => "四川", 52 => "贵州", 53 => "云南", 54 => "西藏", 61 => "陕西", 62 => "甘肃",
63 => "青海", 64 => "宁夏", 65 => "新疆", 71 => "台湾", 81 => "香港", 82 => "澳门", 91 => "国外"
];
/**
** 检查省份是否正确
** @param strin $idCard
** @return void
*/
public function checkProvince($idCard)
{
$provinceNumber = substr($idCard, 0, 2);
return isset($this->provinces[$provinceNumber]);
}
/**
** 检测生日是否正确
** @param strin $idCard
** @return void
*/
public function checkBirthday($idCard)
{
$regx = '#^\d{6}(\d{4})(\d{2})(\d{2})\d{3}[0-9X]$#';
if (!preg_match($regx, $idCard, $matches)) {
return false;
}
array_shift($matches);
list($year, $month, $day) = $matches;
return checkdate($month, $day, $year);
}
/**
** 校验码比对
** @param string $idCard
** @return void
*/
public function checkCode($idCard)
{
$idCardBase = substr($idCard, 0, 17);
$code = $this->genCode($idCardBase);
return $idCard == ($idCardBase . $code);
}
/**
** 身份证号码校验
** @param string $idCard
** @return bool
*/
public function vaild($idCard)
{
// 基础的校验,校验身份证格式是否正确
if (!$this->isCardNumber($idCard)) {
return false;
}
// 将 15 位转换成 18 位
$idCard = $this->fifteen2Eighteen($idCard);
// 检查省是否存在
if (!$this->checkProvince($idCard)) {
return false;
}
// 检查生日是否正确
if (!$this->checkBirthday($idCard)) {
return false;
}
// 检查校验码
return $this->checkCode($idCard);
}
}