PHP校驗15位和18位身份證號

前言

看新公司框架源碼的時候,發(fā)現(xiàn)了這個功能,于是搜索一番并封裝了一下身份證號校驗的類互拾。

目前大家的身份證號大多是 18 位的,當然嚎幸,也不排除有些老人的身份證號是 15 位的颜矿。

如果強制要求是 18 位的話,會比較好嫉晶,因為 15 位的身份證號沒有校驗碼骑疆,可以說,只要了解大概結(jié)構(gòu)替废,隨手都可以造出一系列身份證號碼來箍铭。

當然,如果只是單純的程序校驗椎镣,18 位的身份證號碼也可以偽造诈火,就是需要偽造者花點心思。

最好的還是調(diào)用相關(guān)部門給的接口状答,進行校驗冷守。

本文所編寫的身份證號碼校驗,只是針對相關(guān)規(guī)則下的計算惊科,是調(diào)用接口前能做的事情拍摇。

身份證號規(guī)則

15位: 省份(2位) + 地級市(2位) + 縣級市(2位) + 出生年(2位) + 出生月(2位) + 出生日(2位) + 順序號(3位)

18位: 省份(2位) + 地級市(2位) + 縣級市(2位) + 出生年(4位) + 出生月(2位) + 出生日(2位) + 順序號(3位) + 校驗位(1位)

相比之下,18位15位 多出生年 2位馆截、校驗位 1位授翻。

其中,順序號如果是偶數(shù)孙咪,則說明是女生堪唐,順序號是奇數(shù),則說明是男生翎蹈。

校驗位的計算:

有17位數(shù)字淮菠,分別是:

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

分別用身份證的前 17 位乘以上面相應位置的數(shù)字,然后相加荤堪。

接著用相加的和對 11 取模合陵。

用獲得的值在下面 11 個字符里查找對應位置的字符枢赔,這個字符就是校驗位。

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

15位轉(zhuǎn)18位:

從上述的分析中拥知,可以知道踏拜,只要補充上年分和校驗位就可以了。

一般情況下年份補充都是加上 19 就可以了低剔。

校驗類的實現(xiàn)

通過分析身份證號的規(guī)則速梗,了解到,有幾點是可以做的:

  • 檢查身份是否正確(一般不會變化襟齿,而且省份不多)
  • 檢查地級市和縣級市(如果有這方面的資源姻锁,可以考慮,不過一般不建議)
  • 檢查年月日
  • 檢查校驗碼

當然猜欺,因為可能部分人用的是 15位 的身份證號位隶,所以需要一個轉(zhuǎn)換的方法,不過开皿,這里還是建議限制需要 18位 的身份證號涧黄。

下面開始實現(xiàn):

初始化:

class IDCardFilter
{
    /**
     * 身份證號碼校驗
     *
     * @param  string $idCard
     * @return bool
     */
    public function vaild($idCard)
    {
        // 基礎(chǔ)的校驗,校驗身份證格式是否正確
        if (!$this->isCardNumber($idCard)) {
            return false;
        }

        // 將 15 位轉(zhuǎn)換成 18 位
        $idCard = $this->fifteen2Eighteen($idCard);

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

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

        // 檢查校驗碼
        return $this->checkCode($idCard);
    }
}

上面已經(jīng)實現(xiàn)了一個校驗的方法赋荆,里面調(diào)用了類里的很多方法弓熏,下面一一實現(xiàn)。

檢測是否是身份證號碼:

這一塊的處理比較簡單糠睡,一個正則表達式搞定了挽鞠。

其中,(^\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位轉(zhuǎn)18位:

邏輯不復雜,先判斷是否是15位均抽,然后判斷需要添加的年份嫁赏,最終生成校驗碼拼接返回就OK了。

/**
 * 15位轉(zhuǎn)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);
}

校驗碼的生成:

詳細計算規(guī)則見上面潦蝇,這里就不做重復的闡述了。

/**
 * 生成校驗碼
 *
 * @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 => "內(nèi)蒙古",
    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位 轉(zhuǎn) 18位 的都完全不用考慮這個方法了惋鹅。

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

完整代碼

傳送門:IDCardFilter

最后

這個功能最多算是新穎吧则酝,畢竟之前沒有接觸過。很開心代碼片段里又增加了新的成員闰集。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末沽讹,一起剝皮案震驚了整個濱河市般卑,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌爽雄,老刑警劉巖蝠检,帶你破解...
    沈念sama閱讀 222,807評論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異挚瘟,居然都是意外死亡叹谁,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,284評論 3 399
  • 文/潘曉璐 我一進店門刽沾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人排拷,你說我怎么就攤上這事侧漓。” “怎么了监氢?”我有些...
    開封第一講書人閱讀 169,589評論 0 363
  • 文/不壞的土叔 我叫張陵布蔗,是天一觀的道長。 經(jīng)常有香客問我浪腐,道長纵揍,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,188評論 1 300
  • 正文 為了忘掉前任议街,我火速辦了婚禮泽谨,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘特漩。我一直安慰自己吧雹,他們只是感情好,可當我...
    茶點故事閱讀 69,185評論 6 398
  • 文/花漫 我一把揭開白布涂身。 她就那樣靜靜地躺著雄卷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蛤售。 梳的紋絲不亂的頭發(fā)上丁鹉,一...
    開封第一講書人閱讀 52,785評論 1 314
  • 那天,我揣著相機與錄音悴能,去河邊找鬼揣钦。 笑死,一個胖子當著我的面吹牛漠酿,可吹牛的內(nèi)容都是我干的拂盯。 我是一名探鬼主播,決...
    沈念sama閱讀 41,220評論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼记靡,長吁一口氣:“原來是場噩夢啊……” “哼谈竿!你這毒婦竟也來了团驱?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,167評論 0 277
  • 序言:老撾萬榮一對情侶失蹤空凸,失蹤者是張志新(化名)和其女友劉穎嚎花,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體呀洲,經(jīng)...
    沈念sama閱讀 46,698評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡紊选,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,767評論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了道逗。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片兵罢。...
    茶點故事閱讀 40,912評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖滓窍,靈堂內(nèi)的尸體忽然破棺而出卖词,到底是詐尸還是另有隱情,我是刑警寧澤吏夯,帶...
    沈念sama閱讀 36,572評論 5 351
  • 正文 年R本政府宣布此蜈,位于F島的核電站,受9級特大地震影響噪生,放射性物質(zhì)發(fā)生泄漏裆赵。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,254評論 3 336
  • 文/蒙蒙 一跺嗽、第九天 我趴在偏房一處隱蔽的房頂上張望战授。 院中可真熱鬧,春花似錦桨嫁、人聲如沸陈醒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,746評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽钉跷。三九已至,卻和暖如春肚逸,著一層夾襖步出監(jiān)牢的瞬間爷辙,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,859評論 1 274
  • 我被黑心中介騙來泰國打工朦促, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留膝晾,地道東北人。 一個月前我還...
    沈念sama閱讀 49,359評論 3 379
  • 正文 我出身青樓务冕,卻偏偏與公主長得像血当,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,922評論 2 361

推薦閱讀更多精彩內(nèi)容

  • 為什么有的人身份證最后一位的號碼是0臊旭、1或2落恼、3、4离熏、5佳谦、6、7滋戳、8钻蔑、9或X,而有的人是X這是怎么回事的呢奸鸯,又有多...
    吳富良閱讀 7,535評論 0 1
  • 開發(fā)過程中多多少少都會碰到身份證號碼咪笑,今天在做項目的時候,碰到了對對15位娄涩、18位身份證號碼進行校驗窗怒,借此機會在網(wǎng)...
    追夢小樂閱讀 2,418評論 1 60
  • 我周圍雖不是遍地讀書人,但總有幾個人堅持讀書钝满。我會想兜粘,為什么他們堅持這個習慣申窘?這不禁讓我想到歡樂頌里的安迪弯蚜,她每天...
    過記閱讀 473評論 1 0
  • 吃飯的時候贷洲,我得到一個消息收厨,包過的主管護師需要用將近5萬元換取优构! 當時我就想诵叁,瘋了吧,傻了吧钦椭,換一個證書拧额!冒著風險...
    醉酒言子閱讀 190評論 0 0