身份证号验证(兼容15,18位)

jerry thinkphp 2015年11月18日 收藏
网上有很多类似的方法,我也是到其他网站找到的,我用过一段时间,发觉这个方法有漏洞: 111111111111111 对这个字符串验证结果为真,这是明显的bug,希望有高手帮我修正修正这个bug
  1. /*
  2.  * 身份证号验证(兼容15,18位)
  3.  * 返回数组 status = 0;
  4.  */
  5. function isIdCardNo($idcard) {
  6.     $return = array('status'=>0, 'msg'=>'');
  7.      if( empty($idcard) ){
  8.         $return['msg'] = '输入的身份证号码不能够为空';
  9.         return $return;
  10.     }
  11.     $City = array(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=>"国外");
  12.     $iSum = 0;
  13.     $idCardLength = strlen($idcard);
  14.     //长度验证
  15.     if(!preg_match('/^\d{17}(\d|x)$/i',$idcard) and!preg_match('/^\d{15}$/i',$idcard)) {
  16.         $return['msg'] = L('PUBLIC_IDCARDNO_LIMIT', array('length1'=>'15', 'length2'=>'17'));
  17.         return $return;
  18.     }
  19.     //地区验证
  20.     if(!array_key_exists(intval(substr($idcard,0,2)),$City)) {
  21.         $return['msg'] = '身份证号码的地区编号错误';
  22.         return $return;
  23.     }
  24.     // 15位身份证验证生日,转换为18位
  25.     if ($idCardLength == 15) {
  26.         $sBirthday = '19'.substr($idcard,6,2).'-'.substr($idcard,8,2).'-'.substr($idcard,10,2);
  27.         $d = new DateTime($sBirthday);
  28.         $dd = $d->format('Y-m-d');
  29.         if($sBirthday != $dd) {
  30.             $return['msg'] = '身份证号码的生日错误';
  31.             return $return;
  32.         }
  33.         $idcard = substr($idcard,0,6)."19".substr($idcard,6,9);//15to18
  34.         $Bit18 = getVerifyBit($idcard);//算出第18位校验码
  35.         $idcard = $idcard.$Bit18;
  36.     }
  37.     // 判断是否大于2078年,小于1900年
  38.     $year = substr($idcard,6,4);
  39.     if ($year<1900 || $year>2078 ) {
  40.         $return['msg'] = '身份证号码的出生年份错误';
  41.         return $return;
  42.     }
  43.     //18位身份证处理
  44.     $sBirthday = substr($idcard,6,4).'-'.substr($idcard,10,2).'-'.substr($idcard,12,2);
  45.     $d = new DateTime($sBirthday);
  46.     $dd = $d->format('Y-m-d');
  47.     if($sBirthday != $dd) {
  48.         $return['msg'] = '身份证号码的出生年月日错误';
  49.         return $return;
  50.     }
  51.     //身份证编码规范验证
  52.     $idcard_base = substr($idcard,0,17);
  53.     if(strtoupper(substr($idcard,17,1)) != getVerifyBit($idcard_base)) {
  54.         $return['msg'] = '身份证编码不符合规范验证';
  55.         return $return;
  56.     }
  57.     $return['status'] = 1;
  58.     return $return;
  59. }

  60. // 计算身份证校验码,根据国家标准GB 11643-1999
  61. function getVerifyBit($idcard_base) {
  62.     if (strlen($idcard_base) != 17) {
  63.         return false;
  64.     }
  65.     //加权因子
  66.     $factor = array(7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2);
  67.     //校验码对应值
  68.     $verify_number_list = array('1', '0', 'X', '9', '8', '7', '6', '5', '4','3', '2');
  69.     $checksum = 0;
  70.     for ($i = 0; $i < strlen($idcard_base); $i++) {
  71.         $checksum += substr($idcard_base, $i, 1) * $factor[$i];
  72.     }
  73.     $mod = $checksum % 11;
  74.     $verify_number = $verify_number_list[$mod];
  75.     return $verify_number;
  76. }
添加了个js 版本的,貌似不错
  1.         function checkID(pId){
  2.        //检查身份证号码 Go_Rush(阿舜) from http://ashun.cnblogs.com
  3.         var arrVerifyCode = [1,0,"x",9,8,7,6,5,4,3,2];
  4.         var Wi = [7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2];
  5.         var Checker = [1,9,8,7,6,5,4,3,2,1,1];
  6.         if(pId.length != 15 && pId.length != 18) {
  7.            // return "身份证号共有 15 码或18位";
  8.            return false;
  9.         }

  10.         var Ai=pId.length==18 ? pId.substring(0,17)   :   pId.slice(0,6)+"19"+pId.slice(6,16);

  11.         if (!/^\d+$/.test(Ai)) {
  12.             // return "身份证除最后一位外,必须为数字!";
  13.             return false;
  14.         }

  15.         var yyyy=Ai.slice(6,10) , mm=Ai.slice(10,12)-1 , dd=Ai.slice(12,14);

  16.         var d=new Date(yyyy,mm,dd) , now=new Date();
  17.          var year=d.getFullYear() , mon=d.getMonth() , day=d.getDate();

  18.         if (year!=yyyy || mon!=mm || day!=dd || d>now || year<1940) {
  19.             // return "身份证输入错误!";
  20.             return false;
  21.         }

  22.         for(var i=0,ret=0;i<17;i++) {
  23.             ret+=Ai.charAt(i)*Wi[i];    
  24.         }
  25.          Ai+=arrVerifyCode[ret %=11];     

  26.         // return pId.length ==18 && pId != Ai?"身份证输入错误!":Ai;
  27.         return pId.length ==18 && pId != Ai ? false : true;     
  28.     }