PHP verifies class encapsulation of 15 bit and 18 bit ID numbers

  • 2021-11-10 09:08:28
  • OfStack

When the new company framework source code, found this function, so search 1 times and encapsulated 1 ID card number verification class.

At present, most of everyone's ID numbers are 18 digits. Of course, it is not excluded that some elderly people's ID numbers are 15 digits.

If the mandatory requirement is 18 digits, it will be better, because the 15-digit ID number has no check code. It can be said that as long as you know the general structure, you can create a series of ID numbers at will.

Of course, if it is only a simple program verification, the 18-digit ID number can also be forged, that is, the forger needs to pay some attention.

It is best to call the interface given by relevant departments for verification.

The ID card number verification written in this paper is only aimed at the calculation under relevant rules, which can be done before calling the interface.

ID card number rules

15 digits: province (2 digits) + prefecture-level city (2 digits) + county-level city (2 digits) + birth year (2 digits) + birth month (2 digits) + birth date (2 digits) + sequence number (3 digits)

18 digits: province (2 digits) + prefecture-level city (2 digits) + county-level city (2 digits) + birth year (4 digits) + birth month (2 digits) + birth date (2 digits) + sequence number (3 digits) + check digit (1 digit)

In contrast, 18 bits have 2 bits of birth year and 1 bit of parity bit more than 15 bits.

Among them, if the sequence number is even, it means that it is a girl, and if the sequence number is odd, it means that it is a boy.

Calculation of parity bits:

There are 17 digits, which are:

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

Multiply the first 17 digits of the ID card by the numbers in the corresponding positions above, and then add them up.

Then, 11 is modularized with the sum of the sums.

Use the obtained value to find the corresponding character in the following 11 characters, which is the parity bit.

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

15-bit to 18-bit:

From the above analysis, we can know that it is enough to supplement the previous year's score and check bit.

1 Under normal circumstances, it is enough to add 19 to the year supplement.

Implementation of Verification Class

By analyzing the rules of ID number, we know that there are several things that can be done:

Check whether the identity is correct (1 will not change, and there are not many provinces) Check prefecture-level cities and county-level cities (if there are resources in this area, you can consider it, but 1 is not recommended) Year, month and day of inspection Check check code

Of course, because some people may use 15-digit ID numbers, they need a conversion method. However, it is recommended to limit the need for 18-digit ID numbers.

Start with the following implementation:

Initialization:


class IDCardFilter
{
  /**
   *  ID card number verification 
   *
   * @param string $idCard
   * @return bool
   */
  public function vaild($idCard)
  {
    //  Basic check, check whether the ID card format is correct 
    if (!$this->isCardNumber($idCard)) {
      return false;
    }

    //  Will  15  Bit conversion to  18  Bit 
    $idCard = $this->fifteen2Eighteen($idCard);

    //  Check whether the province exists 
    if (!$this->checkProvince($idCard)) {
      return false;
    }

    //  Check whether the birthday is correct 
    if (!$this->checkBirthday($idCard)) {
      return false;
    }

    //  Check check code 
    return $this->checkCode($idCard);
  }
}

Above has realized a check method, which calls a lot of methods in the class, the following 11 implementation.

Check whether it is an ID number:

The processing of this block is relatively simple, and a regular expression is done.

Where (^\ d {15} $) is used to match the 15-digit ID number; (^\ d {17} (\ dX) $) is used to match the case of an 18-digit ID number.

const REGX = '#(^\d{15}$)|(^\d{17}(\d|X)$)#';


/**
 *  Check whether it is an ID number 
 *
 * @param string $idCard
 * @return boolean
 */
public function isCardNumber($idCard)
{
  return preg_match(self::REGX, $idCard);
}

15-bit to 18-bit:

Logic is not complicated, first judge whether it is 15 bits, then judge the year to be added, and finally generate check code splicing and return to OK.


/**
 * 15 Position rotation 18 Bit 
 *
 * @param string $idCard
 * @return void
 */
public function fifteen2Eighteen($idCard)
{
  if (strlen($idCard) != 15) {
    return $idCard;
  }

  //  If the ID card sequence code is 996 997 998 999 These are special codes for people over 100 years old 
  // $code = array_search(substr($idCard, 12, 3), [996, 997, 998, 999]) !== false ? '18' : '19';
  // 1 Like  19  That's enough 
  $code = '19';
  $idCardBase = substr($idCard, 0, 6) . $code . substr($idCard, 6, 9);
  return $idCardBase . $this->genCode($idCardBase);
}

Generation of check code:

See the above for detailed calculation rules, so we will not repeat them here.


/**
 *  Generate check code 
 *
 * @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];
}

Check whether the provinces are correct:


protected $provinces = [
  11 => " Beijing ", 12 => " Tianjin ", 13 => " Hebei ",  14 => " Shanxi ", 15 => " Inner Mongolia ",
  21 => " Liaoning ", 22 => " Jilin ", 23 => " Heilongjiang ", 31 => " Shanghai ", 32 => " Jiangsu ",
  33 => " Zhejiang ", 34 => " Anhui ", 35 => " Fujian ",  36 => " Jiangxi ", 37 => " Shandong ", 41 => " Henan ",
  42 => " Hubei ", 43 => " Hunan ", 44 => " Guangdong ",  45 => " Guangxi ", 46 => " Hainan ", 50 => " Chongqing ",
  51 => "4 Chuan ", 52 => " Guizhou ", 53 => " Yunnan ",  54 => " Tibet ", 61 => " Shaanxi ", 62 => " Gansu ",
  63 => " Qinghai ", 64 => " Ningxia ", 65 => " Xinjiang ",  71 => " Taiwan ", 81 => " Hong Kong ", 82 => " Macao ", 91 => " Abroad "
];

/**
 *  Check whether the province is correct 
 *
 * @param string $idCard
 * @return void
 */
public function checkProvince($idCard)
{
  $provinceNumber = substr($idCard, 0, 2);
  return isset($this->provinces[$provinceNumber]);
}

Check whether the birthday is correct:

Here, regular matching is also used to match the year, month and day.


/**
 *  Check whether the birthday is correct 
 *
 * @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);
}

Check code comparison:

In other words, 15-bit to 18-bit people don't need to consider this method at all.


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

Complete code

Portal: IDCardFilter

Finally

This function is novel at best, after all, it has not been touched before. I am very happy that new members have been added to the code snippet.


Related articles: