├── .gitignore
├── tests
├── TestCase.php
├── IdentityTest.php
└── Region
│ └── RegionTest.php
├── .travis.yml
├── phpunit.xml
├── composer.json
├── src
├── IdentityInterface.php
├── Region
│ ├── RegionInterface.php
│ └── Region.php
└── Identity.php
├── LICENSE
├── scripts
└── id-card
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor/
2 | composer.lock
3 |
--------------------------------------------------------------------------------
/tests/TestCase.php:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 | ./tests
16 |
17 |
18 |
19 |
20 | ./src
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "medz/id-card-of-china",
3 | "keywords": ["id", "card", "identity", "china"],
4 | "description": "中华人民共和国身份证(The identity card of the people's Republic of China)",
5 | "type": "library",
6 | "license": "MIT",
7 | "authors": [
8 | {
9 | "name": "Seven Du",
10 | "email": "shiweidu@outlook.com"
11 | }
12 | ],
13 | "require": {
14 | "php": ">=7.1.3",
15 | "medz/gb-t-2260": "^2.0"
16 | },
17 | "require-dev": {
18 | "phpunit/phpunit": "^6.5"
19 | },
20 | "autoload": {
21 | "psr-4": {
22 | "Medz\\IdentityCard\\China\\": "src/"
23 | }
24 | },
25 | "autoload-dev": {
26 | "psr-4": {
27 | "Medz\\IdentityCard\\China\\Tests\\": "tests/"
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/IdentityInterface.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | public function legal(): bool;
18 |
19 | /**
20 | * Get Region of The ID Card People.
21 | */
22 | public function region(): RegionInterface;
23 |
24 | /**
25 | * Get The ID Card People Birthday.
26 | *
27 | * @return string
28 | * @author Seven Du
29 | */
30 | public function birthday(): string;
31 |
32 | /**
33 | * Get the ID Card People Gender.
34 | *
35 | * @return string
36 | * @author Seven Du
37 | */
38 | public function gender(): string;
39 | }
40 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Seven Du
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/Region/RegionInterface.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | public function code(): int;
16 |
17 | /**
18 | * Get Province Of The Region.
19 | *
20 | * @return string
21 | * @author Seven Du
22 | */
23 | public function province(): ?string;
24 |
25 | /**
26 | * Get City Of The Region.
27 | *
28 | * @return string
29 | * @author Seven Du
30 | */
31 | public function city(): ?string;
32 |
33 | /**
34 | * Get County Of The Region.
35 | *
36 | * @return string
37 | * @author Seven Du
38 | */
39 | public function county(): ?string;
40 |
41 | /**
42 | * Get The Region Tree.
43 | *
44 | * @return array
45 | * @author Seven Du
46 | */
47 | public function tree(): array;
48 |
49 | /**
50 | * Get The Region Tree String.
51 | *
52 | * @param string $glue Join Array Elements With A Glue String
53 | * @return string
54 | * @author Seven Du
55 | */
56 | public function treeString(string $glue = ''): string;
57 | }
58 |
--------------------------------------------------------------------------------
/scripts/id-card:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | ";
43 | }
44 |
45 | $handle = fopen('php://stdin', 'r');
46 | $identityCardNumber = trim(fgets($handle));
47 | fclose($handle);
48 |
49 | if (!$identityCardNumber) {
50 | echo "> ";
51 | return call_user_func(__FUNCTION__, false);
52 | }
53 |
54 | return $identityCardNumber;
55 | }
56 |
57 | $identityCardNumber = getIdentityCardNumberInStdin(true);
58 | }
59 |
60 | var_dump(class_exists('Identity'));
61 | exit;
62 |
63 | // $identity = new Identity((string) $identityCardNumber);
64 |
65 | var_dump($identity);
66 |
--------------------------------------------------------------------------------
/src/Region/Region.php:
--------------------------------------------------------------------------------
1 |
23 | */
24 | public function __construct(int $regionCode)
25 | {
26 | // Setting init region code.
27 | $this->code = (string) $regionCode;
28 | }
29 |
30 | /**
31 | * Get the Region Code.
32 | *
33 | * @return int
34 | * @author Seven Du
35 | */
36 | public function code(): int
37 | {
38 | return (int) $this->code;
39 | }
40 |
41 | /**
42 | * Get Province Of The Region.
43 | *
44 | * @return string
45 | * @author Seven Du
46 | */
47 | public function province(): ?string
48 | {
49 | return Getter::instance()->province((string) $this->code);
50 | }
51 |
52 | /**
53 | * Get City Of The Region.
54 | *
55 | * @return string
56 | * @author Seven Du
57 | */
58 | public function city(): ?string
59 | {
60 | return Getter::instance()->city((string) $this->code);
61 | }
62 |
63 | /**
64 | * Get County Of The Region.
65 | *
66 | * @return string
67 | * @author Seven Du
68 | */
69 | public function county(): ?string
70 | {
71 | return Getter::instance()->county((string) $this->code);
72 | }
73 |
74 | /**
75 | * Get The Region Tree.
76 | *
77 | * @return array
78 | * @author Seven Du
79 | */
80 | public function tree(): array
81 | {
82 | return array_values(array_filter([
83 | $this->province(),
84 | $this->city(),
85 | $this->county(),
86 | ]));
87 | }
88 |
89 | /**
90 | * Get The Region Tree String.
91 | *
92 | * @param string $glue Join Array Elements With A Glue String
93 | * @return string
94 | * @author Seven Du
95 | */
96 | public function treeString(string $glue = ''): string
97 | {
98 | return implode($glue, $this->tree());
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/tests/IdentityTest.php:
--------------------------------------------------------------------------------
1 | createMock(Identity::class);
19 |
20 | $this->assertTrue($identityMock instanceof IdentityInterface);
21 | }
22 |
23 | /**
24 | * Test Identity legal method.
25 | */
26 | public function testLegal()
27 | {
28 | // Create the Mooc.
29 | $identityMock = $this->getMockBuilder(Identity::class)
30 | ->setConstructorArgs(['350301198906180060'])
31 | ->setMethods(['validateCheckCode'])
32 | ->getMock();
33 |
34 | // Set validateCheckCode method.
35 | $identityMock->expects($this->exactly(2))
36 | ->method('validateCheckCode')
37 | ->will($this->onConsecutiveCalls(true, false));
38 |
39 | $this->assertTrue($identityMock->legal());
40 | $this->assertFalse($identityMock->legal());
41 | }
42 |
43 | /**
44 | * Test Identity birthday method.
45 | */
46 | public function testBirthday()
47 | {
48 | $identityMock = $this->getMockBuilder(Identity::class)
49 | ->setConstructorArgs(['51100019921108'])
50 | ->setMethods(null)
51 | ->getMock();
52 | $this->assertEquals('1992-11-08', $identityMock->birthday());
53 | }
54 |
55 | /**
56 | * Test gender method.
57 | */
58 | public function testGender()
59 | {
60 | $identityMock = $this->getMockBuilder(Identity::class)
61 | ->setConstructorArgs(['0000000000000000100'])
62 | ->setMethods(null)
63 | ->getMock();
64 | $this->assertEquals('男', $identityMock->gender());
65 |
66 | $identityMock = $this->getMockBuilder(Identity::class)
67 | ->setConstructorArgs(['0000000000000000200'])
68 | ->setMethods(null)
69 | ->getMock();
70 | $this->assertEquals('女', $identityMock->gender());
71 | }
72 |
73 | /**
74 | * Test region method.
75 | */
76 | public function testRegion()
77 | {
78 | $identityMock = $this->getMockBuilder(Identity::class)
79 | ->setConstructorArgs(['1100000000000000200'])
80 | ->setMethods(null)
81 | ->getMock();
82 | $this->assertTrue($identityMock->region() instanceof RegionInterface);
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Identity Card Of China (PHP)
2 |
3 | 中华人民共和国身份证(The identity card of the people's Republic of China)
4 |
5 | [](https://travis-ci.com/medz/id-card-of-china)
6 |
7 | > JavaScript 版本:https://github.com/medz/id-card-of-china-js
8 |
9 | `id-card-of-china` 是一个基于「公民身份号码」规则获取公民身份号码中包含的基础信息组件。
10 |
11 | ## 安装
12 |
13 | - 你的 PHP 版本应该 `>= 7.0`
14 |
15 | 我们使用 Composer 安装:
16 |
17 | ```bash
18 | composer require medz/id-card-of-china
19 | ```
20 |
21 | ## 使用
22 |
23 | 遵循 PHP-MD 原则,这个工具不提供静态调用,所以使用需要进行对象实例化:
24 |
25 | ```php
26 | use Medz\IdentityCard\China\Identity;
27 |
28 | $peopleIDNumber = '350301198906180060';
29 | $peopleIdentity = new Identity($peopleIDNumber);
30 | ```
31 |
32 | ## APIs
33 |
34 | > 基于 `Medz\IdentityCard\China\IdentityInterface` 实例
35 |
36 | - `legal`:检查公民身份号码是否合法
37 | - `birthday`:获取公民身份号码中包含的生日信息
38 | - `gender`:获取身份证包含的性别信息
39 | - `region`: 获取身份证包含的地区信息,返回 `Medz\IdentityCard\China\Region\RegionInterface` 实例
40 |
41 | ### `region` 对象 APIs
42 |
43 | - `code`: 获取 GB/T 2260 的地区行政代码
44 | - `province`:获取省份名称或者直辖市名称
45 | - `city`:获取城市名称,如果是直辖市则返回 `''`
46 | - `county`:获取区县名称
47 | - `tree`:获取地区层级数组
48 | - `treeString`:获取地区完整字符串,支持传递一个参数作为省市区的分隔符
49 |
50 | ### 演示
51 |
52 | ```php
53 | use Medz\IdentityCard\China\Identity;
54 |
55 | $peopleIDNumber = '350302198906180060';
56 | $peopleIdentity = new Identity($peopleIDNumber);
57 | $peopleRegion = $peopleIdentity->region();
58 |
59 | var_dump(
60 | $peopleIdentity->legal(), // true | false
61 | $peopleIdentity->birthday(), // 1989-06-18
62 | $peopleIdentity->gender(), // 女 | 男
63 | $peopleRegion->code(), // 350302
64 | $peopleRegion->province(), // 福建省
65 | $peopleRegion->city(), // 莆田市
66 | $peopleRegion->county(), // 城厢区
67 | $peopleRegion->tree(), // ["福建省", "莆田市", "城厢区"]
68 | $peopleRegion->treeString(' ') // 福建省 莆田市 城厢区
69 | );
70 | ```
71 |
72 | ## 疑问解答
73 |
74 | 1. 问:为什么不添加年龄、星座还有属相等信息?
75 |
76 | 答:因为年龄需要专门的时间组件去计算,星座如果是「粗略」计算可以获得,但是没有意义,属相更加复杂,负责任的转换这些属性都比简单转换复杂,简单转换出来的不够准确。同样也失去了包本身该有的功能,例如年龄可以交给「时间组件」等。
77 |
78 | 2. 问:这个包到底干啥的?
79 |
80 | 答:这个包就是提取公民身份号码所包含的基础信息
81 |
82 | 3. 问:可以用来验证人或者其他的吗?
83 |
84 | 答:不基于公安部的 API 验证的身份证组件都不能叫验证组件,现在所有的 ID Card 组件都只能提取公民身份号码所包含的信息,如果说验证,那唯一能验证的就是公民身份号码是否是一个合法的公民身份号码。
85 |
86 | 4. 问:如何获取年龄?
87 | 答:例如在 PHP 中,获取年龄是一个很简单的时区,我用最快的例子,你依赖 `nesbot/carbon` 这个「时间工具」包,依赖代码:
88 | ```bash
89 | composer require nesbot/carbon
90 | ```
91 | 然后看我下面的 PHP 代码:
92 | ```php
93 | use Carbon\Carbon;
94 | $birthday = new Carbon($identity->birthday());
95 | $age = $birthday->diffInYears();
96 | ```
97 | 好了,你打印下 `$age` 试试看!
98 |
99 | ## LICENSE
100 |
101 | This component follows the MIT open source agreement.
102 |
--------------------------------------------------------------------------------
/src/Identity.php:
--------------------------------------------------------------------------------
1 | identityCardNumber = strtoupper($identityCardNumber);
20 | }
21 |
22 | /**
23 | * Check The ID Card is legal.
24 | *
25 | * @return bool
26 | * @author Seven Du
27 | */
28 | public function legal(): bool
29 | {
30 | $regionCode = (int) substr($this->identityCardNumber, 0, 6);
31 |
32 | return ($regionCode >= 110000
33 | && $regionCode <= 820000
34 | && checkdate(
35 | intval(substr($this->identityCardNumber, 10, 2)),
36 | intval(substr($this->identityCardNumber, 12, 2)),
37 | intval(substr($this->identityCardNumber, 6, 4))
38 | )
39 | && $this->validateCheckCode()
40 | );
41 | }
42 |
43 | /**
44 | * Get The ID Card People Birthday.
45 | *
46 | * @return string
47 | * @author Seven Du
48 | */
49 | public function birthday(): string
50 | {
51 | $year = substr($this->identityCardNumber, 6, 4);
52 | $month = substr($this->identityCardNumber, 10, 2);
53 | $day = substr($this->identityCardNumber, 12, 2);
54 |
55 | return sprintf('%s-%s-%s', $year, $month, $day);
56 | }
57 |
58 | /**
59 | * Get the ID Card People Gender.
60 | *
61 | * @return string
62 | * @author Seven Du
63 | */
64 | public function gender(): string
65 | {
66 | return ((intval(substr($this->identityCardNumber, 16, 1)) % 2) === 0) ? '女' : '男';
67 | }
68 |
69 | /**
70 | * Get Region of The ID Card People.
71 | */
72 | public function region(): RegionInterface
73 | {
74 | $regionCode = (int) substr($this->identityCardNumber, 0, 6);
75 |
76 | return new Region($regionCode);
77 | }
78 |
79 | public function validateCheckCode(): bool
80 | {
81 | // Init
82 | $identityCardNumber = $this->identityCardNumber;
83 | $index = $sum = 0;
84 |
85 | // Calculation $sum
86 | for ($index; $index < 17; $index++) {
87 | $sum += ((1 << (17 - $index)) % 11) * intval(substr($identityCardNumber, $index, 1));
88 | }
89 |
90 | // Calculation $quotiety
91 | $quotiety = (12 - ($sum % 11)) % 11;
92 |
93 | if ($quotiety < 10) {
94 | return intval(substr($identityCardNumber, 17, 1)) === $quotiety;
95 | }
96 |
97 | return $quotiety === 10;
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/tests/Region/RegionTest.php:
--------------------------------------------------------------------------------
1 | getMockBuilder(Region::class)
19 | ->disableOriginalConstructor()
20 | // ->setMethods(['setCode'])
21 | ->getMock();
22 |
23 | $this->assertTrue($regionMock instanceof RegionInterface);
24 | }
25 |
26 | /**
27 | * Test __constrcut method.
28 | */
29 | public function testConstructor()
30 | {
31 | $regionMock = new class (110000) extends Region {
32 | public function getConstructorSetCode() {
33 | return $this->code;
34 | }
35 | };
36 |
37 | $this->assertEquals('110000', $regionMock->getConstructorSetCode());
38 | }
39 |
40 | /**
41 | * Test code method.
42 | */
43 | public function testCode()
44 | {
45 | $regionMock = $this->getMockBuilder(Region::class)
46 | ->setConstructorArgs(['110000'])
47 | ->setMethods(null)
48 | ->getMock();
49 | $this->assertEquals(110000, $regionMock->code());
50 | }
51 |
52 | /**
53 | * Test province method
54 | */
55 | public function testProvince()
56 | {
57 | $regionMock = $this->getMockBuilder(Region::class)
58 | ->setConstructorArgs(['110105'])
59 | ->setMethods(null)
60 | ->getMock();
61 | $this->assertEquals('北京市', $regionMock->province());
62 | }
63 |
64 | /**
65 | * Test city method.
66 | */
67 | public function testCity()
68 | {
69 | $regionMock = $this->getMockBuilder(Region::class)
70 | ->setConstructorArgs(['511304'])
71 | ->setMethods(null)
72 | ->getMock();
73 | $this->assertEquals('南充市', $regionMock->city());
74 |
75 | $regionMock = $this->getMockBuilder(Region::class)
76 | ->setConstructorArgs(['110105'])
77 | ->setMethods(null)
78 | ->getMock();
79 | $this->assertEmpty($regionMock->city());
80 | }
81 |
82 | /**
83 | * Test county method.
84 | */
85 | public function testCounty()
86 | {
87 | $regionMock = $this->getMockBuilder(Region::class)
88 | ->setConstructorArgs(['510116'])
89 | ->setMethods(null)
90 | ->getMock();
91 | $this->assertEquals('双流区', $regionMock->county());
92 | }
93 |
94 | /**
95 | * Test tree method.
96 | */
97 | public function testTree()
98 | {
99 | $regionMock = $this->getMockBuilder(Region::class)
100 | ->setConstructorArgs([510116])
101 | ->setMethods(null)
102 | ->getMock();
103 | $this->assertEquals(['四川省', '成都市', '双流区'], $regionMock->tree());
104 |
105 | $regionMock = $this->getMockBuilder(Region::class)
106 | ->setConstructorArgs([110105])
107 | ->setMethods(null)
108 | ->getMock();
109 | $this->assertEquals(['北京市', '朝阳区'], $regionMock->tree());
110 | }
111 |
112 | public function testTreeString()
113 | {
114 | $regionMock = $this->getMockBuilder(Region::class)
115 | ->setConstructorArgs([510116])
116 | ->setMethods(null)
117 | ->getMock();
118 | $this->assertEquals('四川省成都市双流区', $regionMock->treeString());
119 |
120 | $regionMock = $this->getMockBuilder(Region::class)
121 | ->setConstructorArgs([110105])
122 | ->setMethods(null)
123 | ->getMock();
124 | $this->assertEquals('北京市#朝阳区', $regionMock->treeString('#'));
125 | }
126 | }
127 |
--------------------------------------------------------------------------------