PHP 生成身份证号和校验

最近在测试当中需要请求第三方接口,其做了身份证号码的校验,所以不得已需要写一个生成身份证号的方法,起初的行政区划代码(身份证前六位)是写死的,也就是只生成某一地区的身份证号,后来发现中华人民共和国国家标准 GB/T 2260 行政区划代码在 github 上是有保持更新的,所以也就有了现在的方法。

在看代码之前先了解一下身份证号的组成结构:
公民身份号码是特征组合码,由十七位数字本体码和一位校验码组成。排列顺序从左至右依次为:六位数字地址码,八位数字出生日期码,三位数字顺序码和一位数字校验码
结构和形式
  1.号码的结构
  公民身份号码是特征组合码,由十七位数字本体码和一位校验码组成。排列顺序从左至右依次为:六位数字地址码,八位数字出生日期码,三位数字顺序码和一位数字校验码。
  2.地址码
  表示编码对象常住户口所在县(县级市、旗、区)的行政区划代码,按GB/T2260的规定执行。
  3.出生日期码
  表示编码对象出生的年、月、日,按GB/T7408的规定执行,年、月、日代码之间不用分隔符。
  4.顺序码
  表示在同一地址码所标识的区域范围内,对同年、同月、同日出生的人编定的顺序号,顺序码的奇数分配给男性,偶数分配给女性。
  5.校验码
  根据前面十七位数字码,按照ISO 7064:1983.MOD 11-2校验码计算出来的检验码。

计算方法
1、将前面的身份证号码17位数分别乘以不同的系数。从第一位到第十七位的系数分别为:7-9-10-5-8-4-2-1-6-3-7-9-10-5-8-4-2。
2、将这17位数字和系数相乘的结果相加。
3、用加出来和除以11,看余数是多少?
4、余数只可能有0-1-2-3-4-5-6-7-8-9-10这11个数字。其分别对应的最后一位身份证的号码为1-0-X -9-8-7-6-5-4-3-2。(即余数0对应1,余数1对应0,余数2对应X...)
5、通过上面得知如果余数是3,就会在身份证的第18位数字上出现的是9。如果对应的数字是2,身份证的最后一位号码就是罗马数字X。
 

composer.json 中添加:

"cn/gb2260": "dev-master"

生成身份证号的代码如下:
 

/**
 * 随机生成有效的 18 长度的身份证号
 * 中华人民共和国国家标准 GB/T 2260 行政区划代码 https://github.com/cn/GB2260.php/blob/master/README.md
 * @return string
 * @throws \Exception
 */
public static function genID()
{
    $addressCodes = require __DIR__ . '/../vendor/cn/gb2260/src/data.php';//地区编码
    $codes = array_keys($addressCodes);
    $code = $codes[array_rand($codes)];
    //出生年月日
    $year = 1900 + mt_rand(50, 102);//最大是 2002 年
    $month = str_pad(mt_rand(1, 12), 2, '0', STR_PAD_LEFT);
    switch ($month) {
        case '02'://2 月按照 28 天算,简化一下,不算闰年
            $day = str_pad(mt_rand(1, 28), 2, '0', STR_PAD_LEFT);
            break;
        case '04':
        case '06':
        case '09':
        case '11':
            $day = str_pad(mt_rand(1, 30), 2, '0', STR_PAD_LEFT);
            break;
        default:
            $day = str_pad(mt_rand(1, 31), 2, '0', STR_PAD_LEFT);
            break;
    }

    $body = $code . $year . $month . $day . str_pad(mt_rand(1, 999), 3, '1');//身份证前 17 位
    //十七位数字本体码加权求和公式 S = Sum(Ai * Wi), i = 0, … , 16 ,先对前17位数字的权求和
    //Ai:表示第i位置上的身份证号码数字值
    //Wi:表示第i位置上的加权因子 Wi: 7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2
    $weight = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];//身份证的加权因子
    $bodySum = 0;
    //累加body部分与位置加权的积
    for ($i = 0; $i < count($weight); $i++) {
        $bodySum = $bodySum + intval($body[$i]) * $weight[$i];
    }
    $verifyCodes = array('1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2');
    $mod = $bodySum % 11;//余数为 2 时即表示身份证最后一位为 X(表示 10)

//    $sex = $body[16] % 2 == 1 ? '男' : '女';//第 17 为数字为奇数表示 男,否则为 女
//    $address = (new \GB2260\GB2260())->get($code);//根据地区编码获取地址
    return $body . $verifyCodes[$mod];
}

以下是校验身份证号的方法

/**
 * 校验生份证最后一位是否符合规则
 * @param $id
 * @return int|string
 * @throws \Exception
 */
public static function verifyIDNumber($id)
{
    $body = substr($id, 0, 17);//身份证前 17 位
    //十七位数字本体码加权求和公式 S = Sum(Ai * Wi), i = 0, … , 16 ,先对前17位数字的权求和
    //Ai:表示第i位置上的身份证号码数字值
    //Wi:表示第i位置上的加权因子 Wi: 7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2
    $weight = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];//身份证的加权因子
    $bodySum = 0;
    //累加body部分与位置加权的积
    for ($i = 0; $i < count($weight); $i++) {
        $bodySum = $bodySum + intval($body[$i]) * $weight[$i];
    }
    $mod = $bodySum % 11;//余数为 2 时即表示身份证最后一位为 X(表示 10)

    $verifyCodes = array('1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2');

    return substr($id, 17, 1) == $verifyCodes[$mod] ? 1 : "无效的身份证号码:" . $id;
}

 

你可能感兴趣的:(算法,软件测试)