VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > PHP >
  • PHP校验15位和18位身份证号的类封装

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

目前大家的身份证号大多是 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位 的身份证号。

下面开始实现:

初始化:

  1. class IDCardFilter 
  2.   /** 
  3.    * 身份证号码校验 
  4.    * 
  5.    * @param string $idCard 
  6.    * @return bool 
  7.    */ 
  8.   public function vaild($idCard
  9.   { 
  10.     // 基础的校验,校验身份证格式是否正确 
  11.     if (!$this->isCardNumber($idCard)) { 
  12.       return false; 
  13.     } 
  14.  
  15.     // 将 15 位转换成 18 位 
  16.     $idCard = $this->fifteen2Eighteen($idCard); 
  17.  
  18.     // 检查省是否存在 
  19.     if (!$this->checkProvince($idCard)) { 
  20.       return false; 
  21.     } 
  22.  
  23.     // 检查生日是否正确 
  24.     if (!$this->checkBirthday($idCard)) { 
  25.       return false; 
  26.     } 
  27.  
  28.     // 检查校验码 
  29.     return $this->checkCode($idCard); 
  30.   } 

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

检测是否是身份证号码:

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

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

  1. const REGX = '#(^\d{15}$)|(^\d{17}(\d|X)$)#'
  2.  
  3. /** 
  4.  * 检测是否是身份证号码 
  5.  * 
  6.  * @param string $idCard 
  7.  * @return boolean 
  8.  */ 
  9. public function isCardNumber($idCard
  10.   return preg_match(self::REGX, $idCard); 

15位转18位:

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

  1. /** 
  2.  * 15位转18位 
  3.  * 
  4.  * @param string $idCard 
  5.  * @return void 
  6.  */ 
  7. public function fifteen2Eighteen($idCard
  8.   if (strlen($idCard) != 15) { 
  9.     return $idCard
  10.   } 
  11.  
  12.   // 如果身份证顺序码是996 997 998 999,这些是为百岁以上老人的特殊编码 
  13.   // $code = array_search(substr($idCard, 12, 3), [996, 997, 998, 999]) !== false ? '18' : '19'; 
  14.   // 一般 19 就够了 
  15.   $code = '19'
  16.   $idCardBase = substr($idCard, 0, 6) . $code . substr($idCard, 6, 9); 
  17.   return $idCardBase . $this->genCode($idCardBase); 

校验码的生成:

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

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

检查省份是否正确:

  1. protected $provinces = [ 
  2.   11 => "北京", 12 => "天津", 13 => "河北",  14 => "山西", 15 => "内蒙古"
  3.   21 => "辽宁", 22 => "吉林", 23 => "黑龙江", 31 => "上海", 32 => "江苏"
  4.   33 => "浙江", 34 => "安徽", 35 => "福建",  36 => "江西", 37 => "山东", 41 => "河南"
  5.   42 => "湖北", 43 => "湖南", 44 => "广东",  45 => "广西", 46 => "海南", 50 => "重庆"
  6.   51 => "四川", 52 => "贵州", 53 => "云南",  54 => "西藏", 61 => "陕西", 62 => "甘肃"
  7.   63 => "青海", 64 => "宁夏", 65 => "新疆",  71 => "台湾", 81 => "香港", 82 => "澳门", 91 => "国外" 
  8. ]; 
  9.  
  10. /** 
  11.  * 检查省份是否正确 
  12.  * 
  13.  * @param string $idCard 
  14.  * @return void 
  15.  */ 
  16. public function checkProvince($idCard
  17.   $provinceNumber = substr($idCard, 0, 2); 
  18.   return isset($this->provinces[$provinceNumber]); 

检测生日是否正确:

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

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

校验码比对:

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

  1. /** 
  2.  * 校验码比对 
  3.  * 
  4.  * @param string $idCard 
  5.  * @return void 
  6.  */ 
  7. public function checkCode($idCard
  8.   $idCardBase = substr($idCard, 0, 17); 
  9.   $code = $this->genCode($idCardBase); 
  10.   return $idCard == ($idCardBase . $code); 

完整代码

传送门:IDCardFilter

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

 

出处:http://www.phpfensi.com/php/20211101/18334.html


相关教程