|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
/* |
|
4
|
|
|
* This file is part of the godruoyi/laravel-idcard-validator. |
|
5
|
|
|
* |
|
6
|
|
|
* (c) Godruoyi <[email protected]> |
|
7
|
|
|
* |
|
8
|
|
|
* This source file is subject to the MIT license that is bundled. |
|
9
|
|
|
*/ |
|
10
|
|
|
|
|
11
|
|
|
namespace Godruoyi\LaravelIdCard; |
|
12
|
|
|
|
|
13
|
|
|
class IdCard |
|
14
|
|
|
{ |
|
15
|
|
|
/** |
|
16
|
|
|
* 允许的地区码 |
|
17
|
|
|
* |
|
18
|
|
|
* @var array |
|
19
|
|
|
*/ |
|
20
|
|
|
protected static $areaCodes = [ |
|
21
|
|
|
11 => '北京', |
|
22
|
|
|
12 => '天津', |
|
23
|
|
|
13 => '河北', |
|
24
|
|
|
14 => '山西', |
|
25
|
|
|
15 => '内蒙古', |
|
26
|
|
|
21 => '辽宁', |
|
27
|
|
|
22 => '吉林', |
|
28
|
|
|
23 => '黑龙江', |
|
29
|
|
|
31 => '上海', |
|
30
|
|
|
32 => '江苏', |
|
31
|
|
|
33 => '浙江', |
|
32
|
|
|
34 => '安徽', |
|
33
|
|
|
35 => '福建', |
|
34
|
|
|
36 => '江西', |
|
35
|
|
|
37 => '山东', |
|
36
|
|
|
41 => '河南', |
|
37
|
|
|
42 => '湖北', |
|
38
|
|
|
43 => '湖南', |
|
39
|
|
|
44 => '广东', |
|
40
|
|
|
45 => '广西', |
|
41
|
|
|
46 => '海南', |
|
42
|
|
|
50 => '重庆', |
|
43
|
|
|
51 => '四川', |
|
44
|
|
|
52 => '贵州', |
|
45
|
|
|
53 => '云南', |
|
46
|
|
|
54 => '西藏', |
|
47
|
|
|
61 => '陕西', |
|
48
|
|
|
62 => '甘肃', |
|
49
|
|
|
63 => '青海', |
|
50
|
|
|
64 => '宁夏', |
|
51
|
|
|
65 => '新疆', |
|
52
|
|
|
81 => '香港', |
|
53
|
|
|
82 => '澳门', |
|
54
|
|
|
83 => '台湾', |
|
55
|
|
|
]; |
|
56
|
|
|
|
|
57
|
|
|
/** |
|
58
|
|
|
* 安全码 |
|
59
|
|
|
* |
|
60
|
|
|
* @var array |
|
61
|
|
|
*/ |
|
62
|
|
|
protected static $securityCodes = [ |
|
63
|
|
|
1, 0, 'X', 9, 8, 7, 6, 5, 4, 3, 2, |
|
64
|
|
|
]; |
|
65
|
|
|
|
|
66
|
|
|
/** |
|
67
|
|
|
* Determine if the gieved idcard is passed. |
|
68
|
|
|
* |
|
69
|
|
|
* @param string $value |
|
70
|
|
|
* |
|
71
|
|
|
* @return bool |
|
72
|
|
|
*/ |
|
73
|
2 |
|
public static function passes($idcard) |
|
74
|
|
|
{ |
|
75
|
2 |
|
if (!preg_match('/^'.self::resolveMatchRule().'$/', $idcard)) { |
|
76
|
1 |
|
return false; |
|
77
|
|
|
} |
|
78
|
|
|
|
|
79
|
2 |
|
$areaCode = substr($idcard, 0, 2); |
|
80
|
2 |
|
if (!isset(self::$areaCodes[$areaCode])) { |
|
81
|
|
|
return false; |
|
82
|
|
|
} |
|
83
|
|
|
|
|
84
|
2 |
|
$month = (int) substr($idcard, 10, 2); |
|
85
|
2 |
|
$day = (int) substr($idcard, 12, 2); |
|
86
|
2 |
|
$year = (int) substr($idcard, 6, 4); |
|
87
|
|
|
|
|
88
|
2 |
|
if (!checkdate($month, $day, $year)) { |
|
89
|
|
|
return false; |
|
90
|
|
|
} |
|
91
|
|
|
|
|
92
|
2 |
|
$sum = 0; |
|
93
|
|
|
|
|
94
|
2 |
|
for ($i = 17; $i > 0; --$i) { |
|
95
|
2 |
|
$s = pow(2, $i) % 11; |
|
96
|
|
|
|
|
97
|
2 |
|
$sum += $s * $idcard[17 - $i]; |
|
98
|
|
|
} |
|
99
|
|
|
|
|
100
|
2 |
|
return (int) self::$securityCodes[$sum % 11] === (int) $idcard[17]; |
|
101
|
|
|
} |
|
102
|
|
|
|
|
103
|
|
|
/** |
|
104
|
|
|
* Resolve IdCard Validate Rule. |
|
105
|
|
|
* |
|
106
|
|
|
* like '[1-6|8]{1}\d{5}[19|20]{2}\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9xX]{1}' |
|
107
|
|
|
* |
|
108
|
|
|
* @return string |
|
109
|
|
|
*/ |
|
110
|
3 |
|
public static function resolveMatchRule() |
|
111
|
|
|
{ |
|
112
|
3 |
|
$rule = ''; |
|
113
|
|
|
|
|
114
|
3 |
|
foreach (['areacode', 'years', 'months', 'days', 'randoms', 'checkcode'] as $method) { |
|
115
|
3 |
|
$method = sprintf('resolveMatchRuleFor%s', ucfirst($method)); |
|
116
|
3 |
|
$rule .= self::$method(); |
|
117
|
|
|
} |
|
118
|
|
|
|
|
119
|
3 |
|
return $rule; |
|
120
|
|
|
} |
|
121
|
|
|
|
|
122
|
|
|
/** |
|
123
|
|
|
* 地址码规则:. |
|
124
|
|
|
* |
|
125
|
|
|
* 地址码长 6 位 |
|
126
|
|
|
* 以数字 1-9 开头 |
|
127
|
|
|
* 后 5 位为 0-9 的数字 |
|
128
|
|
|
* |
|
129
|
|
|
* @return string |
|
130
|
|
|
*/ |
|
131
|
4 |
|
public static function resolveMatchRuleForAreacode() |
|
132
|
|
|
{ |
|
133
|
4 |
|
return '[1-9]\d{5}'; |
|
134
|
|
|
} |
|
135
|
|
|
|
|
136
|
|
|
/** |
|
137
|
|
|
* 年份码规则:. |
|
138
|
|
|
* |
|
139
|
|
|
* 年份码长 4 位 |
|
140
|
|
|
* 以数字 18,19 或 20 开头,分表表示 18xx 19xx 20xx |
|
141
|
|
|
* 剩余两位为 0-9 的数字 |
|
142
|
|
|
* |
|
143
|
|
|
* @return string |
|
144
|
|
|
*/ |
|
145
|
4 |
|
public static function resolveMatchRuleForYears() |
|
146
|
|
|
{ |
|
147
|
4 |
|
return '(17|18|19|20|21)\d{2}'; |
|
148
|
|
|
} |
|
149
|
|
|
|
|
150
|
|
|
/** |
|
151
|
|
|
* 月份码规则:. |
|
152
|
|
|
* |
|
153
|
|
|
* 月份码长 2 位 |
|
154
|
|
|
* 第一位数字为 0,第二位数字为 1-9 即 01 02 等月份 |
|
155
|
|
|
* |
|
156
|
|
|
* 或者 10 11 12 月份 |
|
157
|
|
|
* |
|
158
|
|
|
* @return string |
|
159
|
|
|
*/ |
|
160
|
4 |
|
public static function resolveMatchRuleForMonths() |
|
161
|
|
|
{ |
|
162
|
4 |
|
return '((0[1-9])|(10|11|12))'; |
|
163
|
|
|
} |
|
164
|
|
|
|
|
165
|
|
|
/** |
|
166
|
|
|
* 日期码规则:. |
|
167
|
|
|
* |
|
168
|
|
|
* 日期码长2位 |
|
169
|
|
|
* 第一位数字为0-2,第二位数字为1-9 即 01 22 11 等 |
|
170
|
|
|
* 或者是10,20,30,31 |
|
171
|
|
|
* |
|
172
|
|
|
* @return string |
|
173
|
|
|
*/ |
|
174
|
4 |
|
public static function resolveMatchRuleForDays() |
|
175
|
|
|
{ |
|
176
|
4 |
|
return '(([0-2][1-9])|10|20|30|31)'; |
|
177
|
|
|
} |
|
178
|
|
|
|
|
179
|
|
|
/** |
|
180
|
|
|
* 随机码规则:. |
|
181
|
|
|
* |
|
182
|
|
|
* 随机码长 3 位 |
|
183
|
|
|
* 随机码是数字 |
|
184
|
|
|
* |
|
185
|
|
|
* @return string |
|
186
|
|
|
*/ |
|
187
|
4 |
|
public static function resolveMatchRuleForRandoms() |
|
188
|
|
|
{ |
|
189
|
4 |
|
return '\d{3}'; |
|
190
|
|
|
} |
|
191
|
|
|
|
|
192
|
|
|
/** |
|
193
|
|
|
* 校验码规则:. |
|
194
|
|
|
* |
|
195
|
|
|
* 校验码长1位 |
|
196
|
|
|
* 可以是数字,字母x或字母X |
|
197
|
|
|
* |
|
198
|
|
|
* @return string |
|
199
|
|
|
*/ |
|
200
|
4 |
|
public static function resolveMatchRuleForCheckcode() |
|
201
|
|
|
{ |
|
202
|
4 |
|
return '[0-9xX]{1}'; |
|
203
|
|
|
} |
|
204
|
|
|
} |
|
205
|
|
|
|