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 相关文章推荐
PHP 变量的定义方法
Jan 26 PHP
PHP中空字符串介绍0、null、empty和false之间的关系
Sep 25 PHP
PHP goto语句简介和使用实例
Mar 11 PHP
php url路由入门实例
Apr 23 PHP
PHP函数getenv简介和使用实例
May 12 PHP
PHP计算一年多少个星期和每周的开始和结束日期
Jul 01 PHP
php数组中删除元素之重新索引的方法
Sep 16 PHP
PHP实现动态web服务器方法
Jul 29 PHP
Yii2.0高级框架数据库增删改查的一些操作
Nov 16 PHP
PHP获取input输入框中的值去数据库比较显示出来
Nov 16 PHP
在Laravel5.6中使用Swoole的协程数据库查询
Jun 15 PHP
PHP利用Mysql锁解决高并发的方法
Sep 04 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生成Flash动画的实现代码
2010/03/12 PHP
php在线解压ZIP文件的方法
2014/12/30 PHP
php生成过去100年下拉列表的方法
2015/07/20 PHP
Yii 使用intervention/image拓展实现图像处理功能
2019/06/22 PHP
php使用fputcsv实现大数据的导出操作详解
2020/02/27 PHP
浅谈angularJS 作用域
2015/07/05 Javascript
js实现获取当前时间是本月第几周的方法
2015/08/11 Javascript
javascript中checkbox使用方法实例演示
2015/11/19 Javascript
JavaScript驾驭网页-获取网页元素
2016/03/24 Javascript
Bootstrap超大屏幕的实现代码
2017/03/22 Javascript
微信小程序之绑定点击事件实例详解
2017/07/07 Javascript
js+html5实现复制文字按钮
2017/07/15 Javascript
vue组件实现可搜索下拉框扩展
2020/10/23 Javascript
使用ng-packagr打包Angular的方法示例
2018/09/21 Javascript
jQuery pager.js 插件动态分页功能实例分析
2019/08/02 jQuery
在vue中实现禁止回退上一步,路由不存历史记录
2020/07/22 Javascript
python简单线程和协程学习心得(分享)
2017/06/14 Python
关于Python中空格字符串处理的技巧总结
2017/08/10 Python
Python 复平面绘图实例
2019/11/21 Python
python3实现弹弹球小游戏
2019/11/25 Python
python opencv如何实现图片绘制
2020/01/19 Python
浅谈django 模型类使用save()方法的好处与注意事项
2020/03/28 Python
基于python模拟bfs和dfs代码实例
2020/11/19 Python
详解CSS3中常用的样式【基本文本和字体样式】
2020/10/20 HTML / CSS
html5 Canvas画图教程(9)—canvas中画出矩形和圆形
2013/01/09 HTML / CSS
HTML5 video播放器全屏(fullScreen)方法实例
2015/04/24 HTML / CSS
澳大利亚排名第一的露营和户外设备在线零售商:Outbax
2020/05/06 全球购物
大学生毕业鉴定
2014/01/31 职场文书
巡警年度自我鉴定
2014/02/21 职场文书
信息技术课后反思
2014/04/27 职场文书
索赔员岗位职责
2015/02/15 职场文书
2015员工年度考核评语
2015/03/25 职场文书
浅谈Golang 嵌套 interface 的赋值问题
2021/04/29 Golang
php7中停止php-fpm服务的方法详解
2021/05/09 PHP
ObjectMapper 如何忽略字段大小写
2021/06/29 Java/Android
SqlServer常用函数及时间处理小结
2023/05/08 SQL Server