PHP Function to Validate Lithuanian Personal Identification Number

Below is a simple function for PHP to check if a number is a valid Lithuanian personal identification number. As I made it for Codeigniter 4, it is included in a class (that can be easily integrated into CI4 framework), but if you want, you can use it as a standalone function.

By default it does not consider valid the numbers with future dates (as people at the time of validation cannot have such numbers). But it might be modified easily to allow for such theoretical numbers.

To check an ID outside the Codeigniter 4 validation context, you would do something like this:

$check = new CustomRules();
$error = '';

$code = '36709010185';
echo $check->valid_lt_id($code, $error) ? 'ok' : $error;
// prints "Lithuanian Personal Identification Number is not valid:
// wrong control number"
$error = '';
$code = '36709010186';
echo $check->valid_lt_id($code, $error) ? 'ok' : $error;
// prints "ok"

To use the class within your Codeigniter 4 project follow the instructions in documentation. You may also want to add translation to the messages if the function should work not only in English (again, see documentation examples for that).

So, here is the class with the relevant function:

/**
 * Codeigniter 4 validation rule for Lithuanian personal idntification number
 */

class CustomRules
{
    public function valid_lt_id($value, ?string &$error = null): bool
    {
        $valid = true;
        $error_msg = '';

        //check length/type
        if ($valid && (strlen($value) !== 11 || !is_numeric($value))) {
            $valid = false;
            $error_msg = 'length not equal to 11 numbers, or not all numbers';
        }

        $digits = str_split($value);

        //check date for validity
        if ($valid) {
            if (in_array($digits[0], [1, 2])) {
                $prefix = '18';
            } elseif (in_array($digits[0], [3, 4])) {
                $prefix = '19';
            } elseif (in_array($digits[0], [5, 6])) {
                $prefix = '20';
            } else {
                $prefix = '21';
            }

            $fullDate = $prefix . substr($value, 1, 6);
            $dateFormat = 'Ymd';

            // Create a DateTime object from the provided date string
            $date = DateTime::createFromFormat($dateFormat, $fullDate);

            // Check if the date is valid and the input format is correct
            $valid = $date && $date->format($dateFormat) === ($fullDate);
            if (!$valid) {
                $error_msg = 'wrong date inside number';
            }
        }

        // check date for not future
        if ($valid) {
            $valid = $fullDate <= date('Ymd');

            if (!$valid) {
                $error_msg = 'date in the future cannot be correct';
            }
        }

        // check control number
        if ($valid) {

            $last = null;
            $s = $digits[0] * 1 + $digits[1] * 2 + $digits[2] * 3
                + $digits[3] * 4 + $digits[4] * 5 + $digits[5] * 6
                + $digits[6] * 7 + $digits[7] * 8 + $digits[8] * 9
                + $digits[9] * 1;

            if (($s % 11) !== 10) {
                $last = $s % 11;
            } else {
                $s = $digits[0] * 3 + $digits[1] * 4 + $digits[2] * 5
                    + $digits[3] * 6 + $digits[4] * 7 + $digits[5] * 8
                    + $digits[6] * 9 + $digits[7] * 1 + $digits[8] * 2
                    + $digits[9] * 3;

                if (($s % 11) !== 10) {
                    $last = $s % 11;
                } else {
                    $last = 0;
                }
            }

            $valid = $digits[10] == $last;
            if (!$valid) {
                $error_msg = 'wrong control number';
            }
        }

        //check if the order number is not zero, should never be
        if ($valid && (string) substr($value, 7, 3) === '000') {
            $valid = false;
            $error_msg = 'wrong registration number';
        }

        if ($valid) {
            return true;
        }
        // $error = lang('myerrors.invalidCode');
        $error = 'Lithuanian Personal Identification Number is not valid: '
            . $error_msg;
        return false;
    }
}

Lithuanian person identification number structure is described in this Wikipedia article. The most complicated part is the last digit – control number. So, if you ever need to fake such numbers for tests, below I provide a function that, upon receiving an otherwise soundly composed personal identification number will spit it out with the 11th digit calculated correctly.

/**
 * This function adds/updates the 11th digit to the Lithuanian Personal
 * Identification Number supplied as parameter to function thus making it
 * a valid number.
 */
function completeLPIN(int $code): int
{
    if (strlen($code) < 10) {
        throw new Exception(
            'Too few digits; Lithuanian personal code has '
                . '11 digits, and this function has to receive at '
                . 'least the first 10.'
        );
        // exception saying "too few numbers" here
    }
    $ak = str_split($code);
    $last = null;

    $s = $ak[0] * 1 + $ak[1] * 2 + $ak[2] * 3 + $ak[3] * 4 + $ak[4] * 5
        + $ak[5] * 6 + $ak[6] * 7 + $ak[7] * 8 + $ak[8] * 9 + $ak[9] * 1;
    if (($s % 11) !== 10) {
        $last = $s % 11;
    } else {
        $s = $ak[0] * 3 + $ak[1] * 4 + $ak[2] * 5 + $ak[3] * 6 + $ak[4] * 7
            + $ak[5] * 8 + $ak[6] * 9 + $ak[7] * 1 + $ak[8] * 2 + $ak[9] * 3;
        if (($s % 11) !== 10) {
            $last = $s % 11;
        } else {
            $last = 0;
        }
    }

    return substr($code, 0, 10)  . $last;
}

echo completeLPIN(3770909012);
// would output:
// 37709090123

Paskelbta

sukūrė

Žymos:

Komentarai

Parašykite komentarą

El. pašto adresas nebus skelbiamas. Būtini laukeliai pažymėti *

Brukalų kiekiui sumažinti šis tinklalapis naudoja Akismet. Sužinokite, kaip apdorojami Jūsų komentarų duomenys.