├── .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 | [![Build Status](https://travis-ci.com/medz/id-card-of-china.svg?branch=master)](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 | --------------------------------------------------------------------------------