PHP校验15位和18位身份证号的类封装


Posted in PHP onNovember 07, 2018

新公司框架源码的时候,发现了这个功能,于是搜索一番并封装了一下身份证号校验的类。

目前大家的身份证号大多是 18 位的,当然,也不排除有些老人的身份证号是 15 位的。

如果强制要求是 18 位的话,会比较好,因为 15 位的身份证号没有校验码,可以说,只要了解大概结构,随手都可以造出一系列身份证号码来。

当然,如果只是单纯的程序校验, 18 位的身份证号码也可以伪造,就是需要伪造者花点心思。

最好的还是调用相关部门给的接口,进行校验。

本文所编写的身份证号码校验,只是针对相关规则下的计算,是调用接口前能做的事情。

身份证号规则

15位:省份(2位) + 地级市(2位) + 县级市(2位) + 出生年(2位) + 出生月(2位) + 出生日(2位) + 顺序号(3位)

18位:省份(2位) + 地级市(2位) + 县级市(2位) + 出生年(4位) + 出生月(2位) + 出生日(2位) + 顺序号(3位) + 校验位(1位)

相比之下, 18位 比 15位 多出生年 2位 、校验位 1位 。

其中,顺序号如果是偶数,则说明是女生,顺序号是奇数,则说明是男生。

校验位的计算:

有17位数字,分别是:

7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2

分别用身份证的前 17 位乘以上面相应位置的数字,然后相加。

接着用相加的和对 11 取模。

用获得的值在下面 11 个字符里查找对应位置的字符,这个字符就是校验位。

'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'

15位转18位:

从上述的分析中,可以知道,只要补充上年分和校验位就可以了。

一般情况下年份补充都是加上 19 就可以了。

校验类的实现

通过分析身份证号的规则,了解到,有几点是可以做的:

  • 检查身份是否正确(一般不会变化,而且省份不多)
  • 检查地级市和县级市(如果有这方面的资源,可以考虑,不过一般不建议)
  • 检查年月日
  • 检查校验码

当然,因为可能部分人用的是 15位 的身份证号,所以需要一个转换的方法,不过,这里还是建议限制需要 18位 的身份证号。

下面开始实现:

初始化:

class IDCardFilter
{
  /**
   * 身份证号码校验
   *
   * @param string $idCard
   * @return bool
   */
  public function vaild($idCard)
  {
    // 基础的校验,校验身份证格式是否正确
    if (!$this->isCardNumber($idCard)) {
      return false;
    }

    // 将 15 位转换成 18 位
    $idCard = $this->fifteen2Eighteen($idCard);

    // 检查省是否存在
    if (!$this->checkProvince($idCard)) {
      return false;
    }

    // 检查生日是否正确
    if (!$this->checkBirthday($idCard)) {
      return false;
    }

    // 检查校验码
    return $this->checkCode($idCard);
  }
}

上面已经实现了一个校验的方法,里面调用了类里的很多方法,下面一一实现。

检测是否是身份证号码:

这一块的处理比较简单,一个正则表达式搞定了。

其中, (^\d{15}$) 用于匹配 15位 身份证号的情况; (^\d{17}(\d|X)$) 用于匹配 18位 身份证号的情况。

const REGX = '#(^\d{15}$)|(^\d{17}(\d|X)$)#';

/**
 * 检测是否是身份证号码
 *
 * @param string $idCard
 * @return boolean
 */
public function isCardNumber($idCard)
{
  return preg_match(self::REGX, $idCard);
}

15位转18位:

逻辑不复杂,先判断是否是15位,然后判断需要添加的年份,最终生成校验码拼接返回就OK了。

/**
 * 15位转18位
 *
 * @param string $idCard
 * @return void
 */
public function fifteen2Eighteen($idCard)
{
  if (strlen($idCard) != 15) {
    return $idCard;
  }

  // 如果身份证顺序码是996 997 998 999,这些是为百岁以上老人的特殊编码
  // $code = array_search(substr($idCard, 12, 3), [996, 997, 998, 999]) !== false ? '18' : '19';
  // 一般 19 就够了
  $code = '19';
  $idCardBase = substr($idCard, 0, 6) . $code . substr($idCard, 6, 9);
  return $idCardBase . $this->genCode($idCardBase);
}

校验码的生成:

详细计算规则见上面,这里就不做重复的阐述了。

/**
 * 生成校验码
 *
 * @param string $idCardBase
 * @return void
 */
final protected function genCode($idCardBase)
{
  $idCardLength = strlen($idCardBase);
  if ($idCardLength != 17) {
    return false;
  }
  $factor = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
  $verifyNumbers = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];
  $sum = 0;
  for ($i = 0; $i < $idCardLength; $i++) {
    $sum += substr($idCardBase, $i, 1) * $factor[$i];
  }
  $index = $sum % 11;
  return $verifyNumbers[$index];
}

检查省份是否正确:

protected $provinces = [
  11 => "北京", 12 => "天津", 13 => "河北",  14 => "山西", 15 => "内蒙古",
  21 => "辽宁", 22 => "吉林", 23 => "黑龙江", 31 => "上海", 32 => "江苏",
  33 => "浙江", 34 => "安徽", 35 => "福建",  36 => "江西", 37 => "山东", 41 => "河南",
  42 => "湖北", 43 => "湖南", 44 => "广东",  45 => "广西", 46 => "海南", 50 => "重庆",
  51 => "四川", 52 => "贵州", 53 => "云南",  54 => "西藏", 61 => "陕西", 62 => "甘肃",
  63 => "青海", 64 => "宁夏", 65 => "新疆",  71 => "台湾", 81 => "香港", 82 => "澳门", 91 => "国外"
];

/**
 * 检查省份是否正确
 *
 * @param string $idCard
 * @return void
 */
public function checkProvince($idCard)
{
  $provinceNumber = substr($idCard, 0, 2);
  return isset($this->provinces[$provinceNumber]);
}

检测生日是否正确:

这里也是用正则匹配,匹配出年月日的。

/**
 * 检测生日是否正确
 *
 * @param string $idCard
 * @return void
 */
public function checkBirthday($idCard)
{
  $regx = '#^\d{6}(\d{4})(\d{2})(\d{2})\d{3}[0-9X]$#';
  if (!preg_match($regx, $idCard, $matches)) {
    return false;
  }
  array_shift($matches);
  list($year, $month, $day) = $matches;
  return checkdate($month, $day, $year);
}

校验码比对:

话说, 15位 转 18位 的都完全不用考虑这个方法了。

/**
 * 校验码比对
 *
 * @param string $idCard
 * @return void
 */
public function checkCode($idCard)
{
  $idCardBase = substr($idCard, 0, 17);
  $code = $this->genCode($idCardBase);
  return $idCard == ($idCardBase . $code);
}

完整代码

传送门:IDCardFilter

最后

这个功能最多算是新颖吧,毕竟之前没有接触过。很开心代码片段里又增加了新的成员。

以上所述是小编给大家介绍的PHP校验15位和18位身份证号的类封装,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

PHP 相关文章推荐
Discuz! 5.0.0论坛程序中加入一段js代码,让会员点击下载附件前自动弹出提示窗口
Apr 18 PHP
php运行出现Call to undefined function curl_init()的解决方法
Nov 02 PHP
探讨file_get_contents与curl效率及稳定性的分析
Jun 06 PHP
深入理解:单一入口、MVC、ORM、CURD、ActiveRecord概念
Jun 06 PHP
wamp安装后自定义配置的方法
Aug 23 PHP
PHP中shuffle数组值随便排序函数用法
Nov 21 PHP
百度工程师讲PHP函数的实现原理及性能分析(一)
May 13 PHP
php简单实现数组分页的方法
Apr 30 PHP
php指定长度分割字符串str_split函数用法示例
Jan 30 PHP
Laravel框架自定义验证过程实例分析
Feb 01 PHP
php策略模式简单示例分析【区别于工厂模式】
Sep 25 PHP
PHP二维数组分页2种实现方法解析
Jul 09 PHP
php中如何执行linux命令详解
Nov 06 #PHP
laravel中的一些简单实用功能
Nov 03 #PHP
详解在YII2框架中使用UEditor编辑器发布文章
Nov 02 #PHP
yii2 上传图片的示例代码
Nov 02 #PHP
yii2实现Ueditor百度编辑器的示例代码
Nov 02 #PHP
php 调用百度sms来发送短信的实现示例
Nov 02 #PHP
Linux下 php7安装redis的方法
Nov 01 #PHP
You might like
php数组添加元素方法小结
2014/12/20 PHP
php计算两个坐标(经度,纬度)之间距离的方法
2015/04/17 PHP
redis查看连接数及php模拟并发创建redis连接的方法
2016/12/15 PHP
Laravel + Elasticsearch 实现中文搜索的方法
2020/02/02 PHP
JavaScript参数个数可变的函数举例说明
2014/10/10 Javascript
jquery中append()与appendto()用法分析
2014/11/14 Javascript
javascript实现微信分享
2014/12/23 Javascript
属于你的jQuery提示框(Tip)插件
2016/01/20 Javascript
javascript实现下雪效果【实例代码】
2016/05/03 Javascript
JS组件Bootstrap Table使用实例分享
2016/05/30 Javascript
从零学习node.js之express入门(六)
2017/02/25 Javascript
jQuery插件echarts实现的去掉X轴、Y轴和网格线效果示例【附demo源码下载】
2017/03/04 Javascript
Vue.js单向绑定和双向绑定实例分析
2018/08/14 Javascript
webpack4+Vue搭建自己的Vue-cli项目过程分享
2018/08/29 Javascript
浅谈vuex中store的命名空间
2019/11/08 Javascript
vue更改数组中的值实例代码详解
2020/02/07 Javascript
electron踩坑之dialog中的callback解决
2020/10/06 Javascript
python错误:AttributeError: 'module' object has no attribute 'setdefaultencoding'问题的解决方法
2014/08/22 Python
Python爬虫辅助利器PyQuery模块的安装使用攻略
2016/04/24 Python
JSON Web Tokens的实现原理
2017/04/02 Python
Python中单、双下划线的区别总结
2017/12/01 Python
Python基于win32ui模块创建弹出式菜单示例
2018/05/09 Python
详解Numpy中的广播原则/机制
2018/09/20 Python
Python/Django后端使用PIL Image生成头像缩略图
2019/04/30 Python
python实现批量修改文件名
2020/03/23 Python
django自定义非主键自增字段类型详解(auto increment field)
2020/03/30 Python
重写django的model下的objects模型管理器方式
2020/05/15 Python
解决Keyerror ''acc'' KeyError: ''val_acc''问题
2020/06/18 Python
俄罗斯香水和化妆品在线商店:Aroma-butik
2020/02/28 全球购物
2014年教师培训的自我评价
2014/01/03 职场文书
报效祖国演讲稿
2014/09/15 职场文书
2014年体育工作总结
2014/11/24 职场文书
2015年团队工作总结范文
2015/05/04 职场文书
科技馆观后感
2015/06/08 职场文书
工作收入证明模板
2015/06/12 职场文书
教研活动主持词
2015/07/03 职场文书