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
|
|
|
|