BelgianNationalNumberValidator::isValid()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace drupol\BelgianNationalNumberFaker\Validator;
6
7
use const FILTER_SANITIZE_NUMBER_INT;
8
9
/**
10
 * Class BelgianNationalNumberValidator.
11
 */
12
final class BelgianNationalNumberValidator
13
{
14
    /**
15
     * Validates a Belgian identification number.
16
     *
17
     * @param string $id
18
     *   The number.
19
     *
20
     * @return bool
21
     *   True if valid, false otherwise.
22
     */
23 2
    public static function isValid(string $id): bool
24
    {
25 2
        return self::isValidNationalNumber($id);
26
    }
27
28
    /**
29
     * Check if the number is a valid national number.
30
     *
31
     * @param string $national_number
32
     *   The number.
33
     *
34
     * @return bool
35
     *   True if validated, false otherwise.
36
     */
37 2
    private static function isValidNationalNumber(string $national_number): bool
38
    {
39 2
        $national_number = filter_var($national_number, FILTER_SANITIZE_NUMBER_INT);
40
41 2
        if (false === $national_number) {
42
            return false;
43
        }
44
45 2
        $allowedCharactersPattern = '/^[0-9\\ \\-]+$/';
46 2
        $nonNumericPattern = '/\\D/';
47
48 2
        if (1 !== preg_match($allowedCharactersPattern, $national_number)) {
49 1
            return false;
50
        }
51
52 2
        $national_number = preg_replace($nonNumericPattern, '', $national_number);
53
54 2
        if (null === $national_number) {
55
            return false;
56
        }
57
58 2
        if (11 !== mb_strlen($national_number)) {
59 1
            return false;
60
        }
61
62 2
        $birthDatePart = (int) mb_substr($national_number, 0, 6);
63 2
        $counterPart = (int) mb_substr($national_number, 6, 3);
64 2
        $controlPart = (int) mb_substr($national_number, 9, 2);
65
66
        // 1. CONTROL NUMBER CHECKING
67 2
        $born2kOrLater = false;
68 2
        $calculatedControl = 97 - (($birthDatePart . $counterPart) % 97);
69
70 2
        if ($calculatedControl !== $controlPart) {
71 2
            $born2kOrLater = true;
72 2
            $calculatedControl = 97 - (('2' . $birthDatePart . $counterPart) % 97);
73
74 2
            if ($calculatedControl !== $controlPart) {
75 2
                return false;
76
            }
77
        }
78
79
        // 2. BIRTHDATE CHECKING
80 2
        $birthDate = true === $born2kOrLater ?
81 2
            '20' . $birthDatePart :
82 2
            '19' . $birthDatePart;
83
84 2
        $checkdate = checkdate(
85 2
            (int) mb_substr($birthDate, 4, 2),
86 2
            (int) mb_substr($birthDate, 6, 2),
87 2
            (int) mb_substr($birthDate, 0, 4)
88
        );
89
90 2
        if (false === $checkdate) {
91 1
            return false;
92
        }
93
94
        // 3. COUNTER CHECKING
95 2
        if (1 > $counterPart || 997 < $counterPart) {
96
            return false;
97
        }
98
99 2
        return true;
100
    }
101
}
102