├── .gitignore ├── tests ├── TestCase.php ├── IdentityTest.php └── Region │ └── RegionTest.php ├── .travis.yml ├── phpunit.xml ├── src ├── IdentityInterface.php ├── Region │ ├── RegionInterface.php │ └── Region.php └── Identity.php ├── composer.json ├── 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 | -------------------------------------------------------------------------------- /src/IdentityInterface.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | public function legal(): bool; 16 | 17 | /** 18 | * Get Region of The ID Card People. 19 | */ 20 | public function region(): RegionInterface; 21 | 22 | /** 23 | * Get The ID Card People Birthday. 24 | * 25 | * @return string 26 | * @author Seven Du 27 | */ 28 | public function birthday(): string; 29 | 30 | /** 31 | * Get the ID Card People Gender. 32 | * 33 | * @return string 34 | * @author Seven Du 35 | */ 36 | public function gender(): string; 37 | } 38 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "phalapi/id-card-of-china", 3 | "keywords": ["id", "card", "identity", "china"], 4 | "description": "中华人民共和国身份证(The identity card of the people's Republic of China). Fork From medz/id-card-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.0", 15 | "medz/gb-t-2260": "^1.0" 16 | }, 17 | "require-dev": { 18 | "phpunit/phpunit": "^6.5" 19 | }, 20 | "autoload": { 21 | "psr-4": { 22 | "PhalApi\\IdentityCard\\China\\": "src/" 23 | } 24 | }, 25 | "autoload-dev": { 26 | "psr-4": { 27 | "PhalApi\\IdentityCard\\China\\Tests\\": "tests/" 28 | } 29 | }, 30 | "config": { 31 | "platform": { 32 | "php": "7.0" 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /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 | 12 | */ 13 | public function code(); 14 | 15 | /** 16 | * Get Province Of The Region. 17 | * 18 | * @return string 19 | * @author Seven Du 20 | */ 21 | public function province(); 22 | 23 | /** 24 | * Get City Of The Region. 25 | * 26 | * @return string 27 | * @author Seven Du 28 | */ 29 | public function city(); 30 | 31 | /** 32 | * Get County Of The Region. 33 | * 34 | * @return string 35 | * @author Seven Du 36 | */ 37 | public function county(); 38 | 39 | /** 40 | * Get The Region Tree. 41 | * 42 | * @return array 43 | * @author Seven Du 44 | */ 45 | public function tree(); 46 | 47 | /** 48 | * Get The Region Tree String. 49 | * 50 | * @param string $glue Join Array Elements With A Glue String 51 | * @return string 52 | * @author Seven Du 53 | */ 54 | public function treeString(string $glue = ''); 55 | } 56 | -------------------------------------------------------------------------------- /scripts/id-card: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | "; 41 | } 42 | 43 | $handle = fopen('php://stdin', 'r'); 44 | $identityCardNumber = trim(fgets($handle)); 45 | fclose($handle); 46 | 47 | if (!$identityCardNumber) { 48 | echo "> "; 49 | return call_user_func(__FUNCTION__, false); 50 | } 51 | 52 | return $identityCardNumber; 53 | } 54 | 55 | $identityCardNumber = getIdentityCardNumberInStdin(true); 56 | } 57 | 58 | var_dump(class_exists('Identity')); 59 | exit; 60 | 61 | // $identity = new Identity((string) $identityCardNumber); 62 | 63 | var_dump($identity); 64 | -------------------------------------------------------------------------------- /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 | 去除PHP严格模式,以免报错。 13 | 14 | ## 安装 15 | 16 | - 你的 PHP 版本应该 `>= 7.0` 17 | 18 | 我们使用 Composer 安装: 19 | 20 | ```bash 21 | composer require phalapi/id-card-of-china 22 | ``` 23 | 24 | ## 使用 25 | 26 | 遵循 PHP-MD 原则,这个工具不提供静态调用,所以使用需要进行对象实例化: 27 | 28 | ```php 29 | use PhalApi\IdentityCard\China\Identity; 30 | 31 | $peopleIDNumber = '350301198906180060'; 32 | $peopleIdentity = new Identity($peopleIDNumber); 33 | ``` 34 | 35 | ## APIs 36 | 37 | > 基于 `PhalApi\IdentityCard\China\IdentityInterface` 实例 38 | 39 | - `legal`:检查公民身份号码是否合法 40 | - `birthday`:获取公民身份号码中包含的生日信息 41 | - `gender`:获取身份证包含的性别信息 42 | - `region`: 获取身份证包含的地区信息,返回 `PhalApi\IdentityCard\China\Region\RegionInterface` 实例 43 | 44 | ### `region` 对象 APIs 45 | 46 | - `code`: 获取 GB/T 2260 的地区行政代码 47 | - `province`:获取省份名称或者直辖市名称 48 | - `city`:获取城市名称,如果是直辖市则返回 `''` 49 | - `county`:获取区县名称 50 | - `tree`:获取地区层级数组 51 | - `treeString`:获取地区完整字符串,支持传递一个参数作为省市区的分隔符 52 | 53 | ### 演示 54 | 55 | ```php 56 | use PhalApi\IdentityCard\China\Identity; 57 | 58 | $peopleIDNumber = '350302198906180060'; 59 | $peopleIdentity = new Identity($peopleIDNumber); 60 | $peopleRegion = $peopleIdentity->region(); 61 | 62 | var_dump( 63 | $peopleIdentity->legal(), // true | false 64 | $peopleIdentity->birthday(), // 1989-06-18 65 | $peopleIdentity->gender(), // 女 | 男 66 | $peopleRegion->code(), // 350302 67 | $peopleRegion->province(), // 福建省 68 | $peopleRegion->city(), // 莆田市 69 | $peopleRegion->county(), // 城厢区 70 | $peopleRegion->tree(), // ["福建省", "莆田市", "城厢区"] 71 | $peopleRegion->treeString(' ') // 福建省 莆田市 城厢区 72 | ); 73 | ``` 74 | 75 | ## 疑问解答 76 | 77 | 1. 问:为什么不添加年龄、星座还有属相等信息? 78 | 79 | 答:因为年龄需要专门的时间组件去计算,星座如果是「粗略」计算可以获得,但是没有意义,属相更加复杂,负责任的转换这些属性都比简单转换复杂,简单转换出来的不够准确。同样也失去了包本身该有的功能,例如年龄可以交给「时间组件」等。 80 | 81 | 2. 问:这个包到底干啥的? 82 | 83 | 答:这个包就是提取公民身份号码所包含的基础信息 84 | 85 | 3. 问:可以用来验证人或者其他的吗? 86 | 87 | 答:不基于公安部的 API 验证的身份证组件都不能叫验证组件,现在所有的 ID Card 组件都只能提取公民身份号码所包含的信息,如果说验证,那唯一能验证的就是公民身份号码是否是一个合法的公民身份号码。 88 | 89 | 4. 问:如何获取年龄? 90 | 答:例如在 PHP 中,获取年龄是一个很简单的时区,我用最快的例子,你依赖 `nesbot/carbon` 这个「时间工具」包,依赖代码: 91 | ```bash 92 | composer require nesbot/carbon 93 | ``` 94 | 然后看我下面的 PHP 代码: 95 | ```php 96 | use Carbon\Carbon; 97 | $birthday = new Carbon($identity->birthday()); 98 | $age = $birthday->diffInYears(); 99 | ``` 100 | 好了,你打印下 `$age` 试试看! 101 | 102 | ## LICENSE 103 | 104 | This component follows the MIT open source agreement. 105 | -------------------------------------------------------------------------------- /src/Identity.php: -------------------------------------------------------------------------------- 1 | identityCardNumber = strtoupper($identityCardNumber); 18 | } 19 | 20 | /** 21 | * Check The ID Card is legal. 22 | * 23 | * @return bool 24 | * @author Seven Du 25 | */ 26 | public function legal(): bool 27 | { 28 | $regionCode = (int) substr($this->identityCardNumber, 0, 6); 29 | 30 | return ($regionCode >= 110000 31 | && $regionCode <= 820000 32 | && checkdate( 33 | intval(substr($this->identityCardNumber, 10, 2)), 34 | intval(substr($this->identityCardNumber, 12, 2)), 35 | intval(substr($this->identityCardNumber, 6, 4)) 36 | ) 37 | && $this->validateCheckCode() 38 | ); 39 | } 40 | 41 | /** 42 | * Get The ID Card People Birthday. 43 | * 44 | * @return string 45 | * @author Seven Du 46 | */ 47 | public function birthday(): string 48 | { 49 | $year = substr($this->identityCardNumber, 6, 4); 50 | $month = substr($this->identityCardNumber, 10, 2); 51 | $day = substr($this->identityCardNumber, 12, 2); 52 | 53 | return sprintf('%s-%s-%s', $year, $month, $day); 54 | } 55 | 56 | /** 57 | * Get the ID Card People Gender. 58 | * 59 | * @return string 60 | * @author Seven Du 61 | */ 62 | public function gender(): string 63 | { 64 | return ((intval(substr($this->identityCardNumber, 16, 1)) % 2) === 0) ? '女' : '男'; 65 | } 66 | 67 | /** 68 | * Get Region of The ID Card People. 69 | */ 70 | public function region(): RegionInterface 71 | { 72 | $regionCode = (int) substr($this->identityCardNumber, 0, 6); 73 | 74 | return new Region($regionCode); 75 | } 76 | 77 | public function validateCheckCode(): bool 78 | { 79 | // Init 80 | $identityCardNumber = $this->identityCardNumber; 81 | $index = $sum = 0; 82 | 83 | // Calculation $sum 84 | for ($index; $index < 17; $index++) { 85 | $sum += ((1 << (17 - $index)) % 11) * intval(substr($identityCardNumber, $index, 1)); 86 | } 87 | 88 | // Calculation $quotiety 89 | $quotiety = (12 - ($sum % 11)) % 11; 90 | 91 | if ($quotiety < 10) { 92 | return intval(substr($identityCardNumber, 17, 1)) === $quotiety; 93 | } 94 | 95 | return $quotiety === 10; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/Region/Region.php: -------------------------------------------------------------------------------- 1 | 26 | */ 27 | public function __construct(int $regionCode) 28 | { 29 | if (empty(static::$regions)) { 30 | // Seting regions to [static::$regions], 31 | // Using `json_decode` function decode json RAW string. 32 | static::$regions = json_decode( 33 | // Using `file_get_contents` function read 34 | // `medz/gb-t-2600` package data provided. 35 | file_get_contents(MEDZ_GBT2260_RAW_PATH), true 36 | ); 37 | } 38 | 39 | // Setting init region code. 40 | $this->code = (string) $regionCode; 41 | } 42 | 43 | /** 44 | * Get the Region Code. 45 | * 46 | * @return int 47 | * @author Seven Du 48 | */ 49 | public function code() 50 | { 51 | return (int) $this->code; 52 | } 53 | 54 | /** 55 | * Get Province Of The Region. 56 | * 57 | * @return string 58 | * @author Seven Du 59 | */ 60 | public function province() 61 | { 62 | $provinceCode = substr($this->code, 0, 2).'0000'; 63 | 64 | if(isset(static::$regions[$provinceCode])){ 65 | return static::$regions[$provinceCode]; 66 | } 67 | return ''; 68 | 69 | } 70 | 71 | /** 72 | * Get City Of The Region. 73 | * 74 | * @return string 75 | * @author Seven Du 76 | */ 77 | public function city() 78 | { 79 | // Get city code of the region. 80 | $cityCode = substr($this->code, 0, 4).'00'; 81 | 82 | if (isset(static::$regions[$cityCode])) { 83 | return static::$regions[$cityCode]; 84 | } 85 | 86 | return ''; 87 | } 88 | 89 | /** 90 | * Get County Of The Region. 91 | * 92 | * @return string 93 | * @author Seven Du 94 | */ 95 | public function county() 96 | { 97 | if(isset(static::$regions[$this->code])){ 98 | return static::$regions[$this->code]; 99 | } 100 | return ''; 101 | } 102 | 103 | /** 104 | * Get The Region Tree. 105 | * 106 | * @return array 107 | * @author Seven Du 108 | */ 109 | public function tree() 110 | { 111 | return array_values(array_filter([ 112 | $this->province(), 113 | $this->city(), 114 | $this->county(), 115 | ])); 116 | } 117 | 118 | /** 119 | * Get The Region Tree String. 120 | * 121 | * @param string $glue Join Array Elements With A Glue String 122 | * @return string 123 | * @author Seven Du 124 | */ 125 | public function treeString(string $glue = '') 126 | { 127 | return implode($glue, $this->tree()); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------