Completed
Push — master ( 0ede2f...e454b6 )
by Yang
02:20
created

IDCard::sex()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 3
eloc 2
nc 4
nop 0
1
<?php
2
3
namespace Leonis\IDCard;
4
5
class IDCard
6
{
7
    /**
8
     * 身份证号.
9
     *
10
     * @var string
11
     */
12
    private $id;
13
14
    /**
15
     * 行政区划代码
16
     *
17
     * @var array
18
     */
19
    private $areaCodes;
20
21
    public function __construct(string $id)
22
    {
23
        $this->id = strtoupper($id);
24
        $this->areaCodes = (array) require __DIR__.'/../data/codes.php';
25
    }
26
27
    /**
28
     * 验证身份号.
29
     *
30
     * @return bool
31
     */
32
    public function check()
33
    {
34
        return ($this->checkAreaCode() and $this->checkBirthday() and $this->checkCode()) ? true : false;
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
35
    }
36
37
    /**
38
     * 验证行政区划代码
39
     *
40
     * @return bool
41
     */
42
    public function checkAreaCode()
43
    {
44
        $areaCode = substr($this->id, 0, 6);
45
46
        return array_key_exists($areaCode, $this->areaCodes);
47
    }
48
49
    /**
50
     * 验证生日.
51
     *
52
     * @return bool
53
     */
54
    public function checkBirthday()
55
    {
56
        $year = substr($this->id, 6, 4);
57
        $month = substr($this->id, 10, 2);
58
        $day = substr($this->id, 12, 2);
59
60
        return checkdate($month, $day, $year);
61
    }
62
63
    /**
64
     * 验证校验码
65
     *
66
     * @return bool
67
     */
68
    public function checkCode()
69
    {
70
        $weight = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
71
        $codes = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];
72
        $code = substr($this->id, 17, 1);
73
        $sum = 0;
74
        for ($i = 0; $i < 17; $i++) {
75
            $sum += substr(substr($this->id, 0, 17), $i, 1) * $weight[$i];
76
        }
77
78
        return $code == $codes[$sum % 11];
79
    }
80
81
    /**
82
     * 获取地址
83
     *
84
     * @param string $separator
85
     *
86
     * @return bool|string
87
     */
88
    public function address(string $separator = '')
89
    {
90
        return $this->check() ? $this->province().$separator.$this->city().$separator.$this->zone() : false;
91
    }
92
93
    /**
94
     * 获取省
95
     *
96
     * @return bool|mixed
97
     */
98
    public function province()
99
    {
100
        $provinceCode = substr($this->id, 0, 2).'0000';
101
102
        return $this->check() ? $this->areaCodes[$provinceCode] : false;
103
    }
104
105
    /**
106
     * 获取市
107
     *
108
     * @return bool|mixed
109
     */
110
    public function city()
111
    {
112
        $cityCode = substr($this->id, 0, 4).'00';
113
114
        return $this->check() ? $this->areaCodes[$cityCode] : false;
115
    }
116
117
    /**
118
     * 获取区.
119
     *
120
     * @return bool|mixed
121
     */
122
    public function zone()
123
    {
124
        $areaCode = substr($this->id, 0, 6);
125
126
        return $this->check() ? $this->areaCodes[$areaCode] : false;
127
    }
128
129
    /**
130
     * 获取生日.
131
     *
132
     * @param string $format
133
     *
134
     * @return bool|string
135
     */
136
    public function birthday(string $format)
137
    {
138
        return date($format, strtotime($this->year().'-'.$this->month().'-'.$this->day()));
139
    }
140
141
    /**
142
     * 获取年.
143
     *
144
     * @return int
145
     */
146
    public function year()
147
    {
148
        return (int) substr($this->id, 6, 4);
149
    }
150
151
    /**
152
     * 获取月.
153
     *
154
     * @return int
155
     */
156
    public function month()
157
    {
158
        return (int) substr($this->id, 10, 2);
159
    }
160
161
    /**
162
     * 获取日.
163
     *
164
     * @return int
165
     */
166
    public function day()
167
    {
168
        return (int) substr($this->id, 12, 2);
169
    }
170
171
    /**
172
     * 获取年龄.
173
     *
174
     * @return false|int|string
175
     */
176
    public function age()
177
    {
178
        if (!$this->check()) {
179
            return false;
180
        }
181
182
        $year = $this->year();
183
        $month = $this->month();
184
        $day = $this->day();
185
186
        $nowYear = (int)date('Y');
187
        $nowMonth = (int)date('n');
188
        $nowDay = (int)date('j');
189
190
        $age = 0;
191
        if ($nowYear > $year) {
192
            $age = $nowYear - $year - 1;
193
            if ($nowMonth > $month) {
194
                $age++;
195
            } elseif ($nowMonth === $month) {
196
                if ($nowDay >= $day) {
197
                    $age++;
198
                }
199
            }
200
        }
201
202
        return $age;
203
    }
204
205
    /**
206
     * 获取性别.
207
     *
208
     * @return bool|string 身份证号未通过验证返回 false
209
     */
210
    public function sex()
211
    {
212
        return $this->check() ? (substr($this->id, 16, 1) % 2 === 0 ? '女' : '男') : false;
213
    }
214
215
    /**
216
     * 获取星座.
217
     *
218
     * @return bool|mixed
219
     */
220
    public function constellation()
221
    {
222
        if (!$this->check()) {
223
            return false;
224
        }
225
226
        $constellation = ['水瓶座', '双鱼座', '白羊座', '金牛座', '双子座', '巨蟹座', '狮子座', '处女座', '天秤座', '天蝎座', '射手座', '魔羯座'];
227
        $constellationDays = [21, 20, 21, 20, 21, 22, 23, 23, 23, 24, 22, 21];
228
229
        $month = $this->month() - 1;
230
        $day = $this->day();
231
232
        if ($day < $constellationDays[$month]) {
233
            $month--;
234
        }
235
236
        return $month >= 0 ? $constellation[$month] : $constellation[11];
237
    }
238
239
    /**
240
     * 获取属相.
241
     *
242
     * @return bool|string
243
     */
244
    public function zodiac()
245
    {
246
        if (!$this->check()) {
247
            return false;
248
        }
249
250
        $year = $this->year();
251
        $index = $year > 1901 ? ($year - 1901) % 12 : (1901 - $year) % 12;
252
        $zodiac = ['牛', '虎', '兔', '龙', '蛇', '马', '羊', '猴', '鸡', '狗', '猪', '鼠'];
253
254
        return $zodiac[$index];
255
    }
256
}
257