phpyk
9/13/2018 - 9:11 AM

检测身份证号合法性

获取出生日期、性别 检测是否合法(中国大陆、香港、台湾、澳门) 检测是否为护照

<?php
/**
 * 身份证验证
 */
namespace App\Utils\Filters;

class FilterIdCard
{

    private $_birth_year;
    private $_birth_month;
    private $_birth_date;
    private $_gender = 1;

    /**
     * 获取出生年
     * @return int
     */
    public function getBirthYear()
    {
        return intval($this->_birth_year);
    }

    /**
     * 获取出生月
     * @retur string
     */
    public function getBirthMonth()
    {
        return $this->_birth_month;
    }

    /**
     * 获取出生日期
     * @return string
     */
    public function getBirthDate()
    {
        return $this->_birth_date;
    }

    /**
     * 获取性别 1=男, 2=女
     * @return int
     */
    public function getGender()
    {
        return $this->_gender;
    }

    public function filter($id_card)
    {
        $this->_birth_year = '';
        $card_no = trim($id_card);
        if($this->isChinaCard($card_no))
        {
            return $card_no;
        }elseif($this->isAgentNumber($card_no))
        {
            return $card_no;
        }elseif($this->isHongKongCard($card_no))
        {
            return $card_no;
        }elseif($this->isTaiWanCard($card_no))
        {
            return $card_no;
        }elseif($this->isMacaoCard($card_no))
        {
            return $card_no;
        }elseif($this->isPassport($card_no))
        {
            return $card_no;
        }elseif($this->isTaiBaoCard($card_no)){
            return substr($card_no, 3);
        }else
        {
            return false;
        }
    }

    // 计算身份证校验码,根据国家标准GB 11643-1999
    function idcard_verify_number($idcard_base)
    {
        if(strlen($idcard_base) != 17)
        {
            return false;
        }
        //加权因子
        $factor = array(7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2);
        //校验码对应值
        $verify_number_list = array('1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2');
        $checksum = 0;
        for ($i = 0; $i < strlen($idcard_base); $i++)
        {
            $checksum += substr($idcard_base, $i, 1) * $factor[$i];
        }
        $mod = $checksum % 11;
        $verify_number = $verify_number_list[$mod];
        return $verify_number;
    }

    // 将15位身份证升级到18位
    function idcard_15to18($idcard){
        if (strlen($idcard) != 15){
            return false;
        }else{
            // 如果身份证顺序码是996 997 998 999,这些是为百岁以上老人的特殊编码
            if (array_search(substr($idcard, 12, 3), array('996', '997', '998', '999')) !== false){
                $idcard = substr($idcard, 0, 6) . '18'. substr($idcard, 6, 9);
            }else{
                $idcard = substr($idcard, 0, 6) . '19'. substr($idcard, 6, 9);
            }
        }
        $idcard = $idcard . $this->idcard_verify_number($idcard);
        return $idcard;
    }

    // 18位身份证校验码有效性检查
    function idcard_checksum18($idcard){
        if (strlen($idcard) != 18){ return false; }
        $idcard_base = substr($idcard, 0, 17);
        if ($this->idcard_verify_number($idcard_base) != strtoupper(substr($idcard, 17, 1))){
            return false;
        }else{
            return $idcard;
        }
    }

    /**
     * 是否中国大陆身份证号
     * @param string $card_no 身份证号
     * @return bool
     */
    public function isChinaCard($card_no)
    {
        $card_no = strtoupper($card_no);
        if(strlen($card_no) == 15)
        {
            $r = substr($card_no, 14, 1);
            $card_no = $this->idcard_15to18($card_no);
        }else
        {
            $r = substr($card_no, 16, 1);
        }
        $this->_gender = ($r%2==0) ? 2 : 1;
        //$card_no = strlen($card_no) == 15 ? $this->idcard_15to18($card_no) : $card_no;
        $this->_birth_year = substr($card_no, 6, 4);
        $this->_birth_month = substr($card_no, 10, 2);
        $this->_birth_date = substr($card_no, 12, 2);
        return $this->idcard_checksum18($card_no);
    }

    /**
     * 是否开头是万色店主编号的号码
     * @param string $card_no 编号
     * @return bool
     */
    public function isAgentNumber($card_no)
    {
        $letter = strtoupper(substr($card_no, 0 ,2));
        return $letter == 'FS' ? true : false;
    }

    /**
     * 是否中国香港身份证号
     * @param string $card_no 身份证号
     * @return bool
     */
    public function isHongKongCard($card_no)
    {
        $card_no = str_replace(array('(', ')'), array('(', ')'), $card_no);
        $pattern = '/^[a-z]\d{2,7}\([\da]\)$/i';
        $match = preg_match($pattern, $card_no);
        if($match)
        {
            $alpha = strtolower($card_no[0]);
            $first = ord($alpha) - 96;
            $sum = $first*8 + $card_no[1]*7 + $card_no[2]*6 + $card_no[3]*5 + $card_no[4]*4 + $card_no[5]*3 + $card_no[6]*2;
            if(intval($card_no[8]) == 11 - $sum % 11)
            {
                return true;
            }
        }
        return false;
    }

    /**
     * 是否中国台湾身份证号
     * @param string $card_no 身份证号
     * @return bool
     */
    public function isTaiWanCard($card_no)
    {
        $pattern = '/^[a-z]\d{9}$/i';
        $match = preg_match($pattern, $card_no);
        if($match)
        {
            $sum = $card_no[1]*8 + $card_no[2]*7 + $card_no[3]*6 + $card_no[4]*5 + $card_no[5]*4 + $card_no[6]*3 + $card_no[7]*2 + $card_no[8]*1 + $card_no[9];
            if(intval($card_no[9]) == $sum % 10)
            {
                return true;
            }
        }
        return false;
    }

    /**
     * 输入以TBZ开头,的8-10位数字判定为台胞证
     * @param string $card_no
     * @return bool
     */
    public function isTaiBaoCard($card_no)
    {
        $pattern = '/^TBZ\d{8,10}$/i';
        $match = preg_match($pattern, $card_no);
        return $match ? true : false;
    }

    /**
     * 是否中国澳门身份证号
     * @param string $card_no 身份证号
     * @return bool
     */
    public function isMacaoCard($card_no)
    {
        $card_no = str_replace(array('(', ')'), array('(', ')'), $card_no);
        $pattern = '/^(?:1|5|7)\d{7}\(\d\)$/i';
        $match = preg_match($pattern, $card_no);
        return $match ? true : false;
    }

    /**
     * 是否护照号
     * @param string $card_no 身份证号
     * @return bool
     */
    public function isPassport($card_no)
    {
        if(strlen($card_no) == 9 && substr($card_no, 0, 1) != '0')
        {
            $letter = strtoupper(substr($card_no, 0 ,4));
            if(!in_array($letter, array('1000','0200','0600'))) return true;

        }
        return false;
    }

    //检查身份证合法性
    public function checkIdCard($idcard){
        if(empty($idcard)){return false;}
        $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=>"国外");
        $iSum = 0;
        $idCardLength = strlen($idcard);
        //长度验证
        if(!preg_match('/^(^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$)|(^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])((\d{4})|\d{3}[Xx])$)$/',$idcard)){
            return false;
        }
            //地区验证
        if(!array_key_exists(intval(substr($idcard,0,2)),$City)){
            return false;
        }
            // 15位身份证验证生日,转换为18位
        if ($idCardLength == 15){
            return false;
            // $sBirthday = '19'.substr($idcard,6,2).'-'.substr($idcard,8,2).'-'.substr($idcard,10,2);
            // $dd = date('Y-m-d', strtotime($sBirthday));
            // if($sBirthday != $dd){
            //     return false;
            // }
            // $idcard = substr($idcard,0,6)."19".substr($idcard,6,9);//15to18
            // $Bit18 = $this->getVerifyBit($idcard);//算出第18位校验码
            // $idcard = $idcard.$Bit18;
        }
            // 判断是否大于2078年,小于1900年
        $year = substr($idcard,6,4);
        if ($year<1900 || $year>2078 ){
            return false;
        }

        //18位身份证处理
        $sBirthday = substr($idcard,6,4).'-'.substr($idcard,10,2).'-'.substr($idcard,12,2);
        $dd = date('Y-m-d', strtotime($sBirthday));
        if($sBirthday != $dd){
           return false;
        }
            //身份证编码规范验证
        $idcard_base = substr($idcard,0,17);
        if(strtoupper(substr($idcard,17,1)) != $this->getVerifyBit($idcard_base)){
            return false;
        }
        return true;
    }

    // 计算身份证校验码,根据国家标准GB 11643-1999
    protected  function getVerifyBit($idcard_base){
        if(strlen($idcard_base) != 17){
            return false;
        }
        //加权因子
        $factor = array(7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2);
        //校验码对应值
        $verify_number_list = array('1', '0', 'X', '9', '8', '7', '6', '5', '4','3', '2');
        $checksum = 0;
        for ($i = 0; $i < strlen($idcard_base); $i++){
                $checksum += substr($idcard_base, $i, 1) * $factor[$i];
        }
        $mod = $checksum % 11;
        $verify_number = $verify_number_list[$mod];
        return $verify_number;
    }
}