Issues (23)

src/VerityChineseIDNumber.php (4 issues)

1
<?php
2
/**
3
 * Created by PhpStorm.
4
 * User: zhanglingyu
5
 * Date: 2019-02-20
6
 * Time: 10:53
7
 */
8
9
namespace ArcherZdip\Identity;
10
11
use Exception;
12
use DateInterval;
13
use DateTimeImmutable;
14
15
class VerityChineseIDNumber
16
{
17
    const PATTERN = '/^(\d{6})+(\d{4})+(\d{2})+(\d{2})+(\d{2})(\d)([0-9]|X)$/';
18
19
    /** @var string|null $idNumber */
20
    public $idNumber = null;
21
22
    /** @var $year */
0 ignored issues
show
Documentation Bug introduced by
The doc comment $year at position 0 could not be parsed: Unknown type name '$year' at position 0 in $year.
Loading history...
23
    public $year;
24
25
    /** @var $month */
0 ignored issues
show
Documentation Bug introduced by
The doc comment $month at position 0 could not be parsed: Unknown type name '$month' at position 0 in $month.
Loading history...
26
    public $month;
27
28
    /** @var $day */
0 ignored issues
show
Documentation Bug introduced by
The doc comment $day at position 0 could not be parsed: Unknown type name '$day' at position 0 in $day.
Loading history...
29
    public $day;
30
31
    /** @var $sexPosition */
0 ignored issues
show
Documentation Bug introduced by
The doc comment $sexPosition at position 0 could not be parsed: Unknown type name '$sexPosition' at position 0 in $sexPosition.
Loading history...
32
    public $sexPosition;
33
34
    /**
35
     * 真实但无效的身份证号码
36
     *
37
     * @var array
38
     */
39
    public static $trustedIdNumbers = [];
40
41
    /**
42
     * VerityChineseIDNumber constructor.
43
     * @param string $idNumber
44
     * @throws Exception
45
     */
46
    public function __construct(string $idNumber)
47
    {
48
        $this->idNumber = $idNumber;
49
        $this->parseData($this->idNumber);
50
    }
51
52
    /**
53
     * valid chinese id number.
54
     *
55
     * @param $idNumber
56
     * @return bool
57
     */
58
    public static function isValid($idNumber)
59
    {
60
        try {
61
            new static($idNumber);
62
            return true;
63
        } catch (Exception $exception) {
64
            return false;
65
        }
66
    }
67
68
    /**
69
     * 获取生日
70
     *
71
     * @param $tz
72
     * @return DateTimeImmutable
73
     * @throws \Exception
74
     */
75
    public function getBirthday($tz = null)
76
    {
77
        $time = date('Y-m-d', mktime(0, 0, 0, $this->month, $this->day, $this->year));
78
79
        return new DateTimeImmutable($time, $tz);
80
    }
81
82
    /**
83
     * 获取年龄
84
     *
85
     * @param bool $strict 是否为严格模式
86
     * @return int
87
     * @throws \Exception
88
     */
89
    public function getAge($strict = true)
90
    {
91
        $today = (new DateTimeImmutable('today'));
92
        $birthday = $this->getBirthday();
93
94
        if ($strict) {
95
            $age = $today->diff($birthday)->y;
96
            if ($birthday > $today->sub(new DateInterval("P{$age}Y"))) {
97
                $age++;
98
            }
99
        } else {
100
            $age = abs((int)$today->format('Y') - (int)$birthday->format('Y'));
101
        }
102
103
        return $age;
104
    }
105
106
    /**
107
     * 是否为男性
108
     *
109
     * @return bool
110
     */
111
    public function isMale()
112
    {
113
        return $this->sexPosition % 2 !== 0;
114
    }
115
116
    /**
117
     * 是否为女性
118
     *
119
     * @return bool
120
     */
121
    public function isFemale()
122
    {
123
        return $this->sexPosition % 2 === 0;
124
    }
125
126
    /**
127
     * 获取年份
128
     *
129
     * @return int
130
     */
131
    public function getYear()
132
    {
133
        return $this->year;
134
    }
135
136
    /**
137
     * 获取月份
138
     *
139
     * @return int
140
     */
141
    public function getMonth()
142
    {
143
        return $this->month;
144
    }
145
146
    /**
147
     * 获取日期
148
     *
149
     * @return int
150
     */
151
    public function getDay()
152
    {
153
        return $this->day;
154
    }
155
156
    /**
157
     * @param string $idNumber
158
     * @return bool
159
     */
160
    protected function verify(string $idNumber)
161
    {
162
        // 检验18位身份证的校验码是否正确。
163
        // 校验位按照ISO 7064:1983.MOD 11-2的规定生成,X可以认为是数字10。
164
        $map = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];
165
166
        $sum = 0;
167
        for ($i = 17; $i > 0; $i--) {
168
            $n = pow(2, $i) % 11;
169
            $sum += $n * (int)$idNumber[17 - $i];
170
        }
171
172
        return $map[$sum % 11] === $idNumber[17];
173
    }
174
175
    /**
176
     * @param string $idNumber
177
     * @return $this|false
178
     * @throws Exception
179
     */
180
    public function parseData(string $idNumber)
181
    {
182
        if (preg_match(static::PATTERN, $idNumber, $matches)) {
183
            // 检查生日日期是否正确
184
            $birthday = $matches[2] . '/' . $matches[3] . '/' . $matches[4];
185
186
            if (strtotime($birthday)) {
187
                $this->year = (int)$matches[2];
188
                $this->month = (int)$matches[3];
189
                $this->day = (int)$matches[4];
190
                $this->sexPosition = (int)$matches[6];
191
192
                if (in_array($idNumber, static::$trustedIdNumbers, true)) {
193
                    return $this;
194
                }
195
196
                if ($this->verify($idNumber)) {
197
                    return $this;
198
                }
199
            }
200
        }
201
202
        throw new Exception('无效的身份证号码。');
203
    }
204
}