-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathNationalIdentificationNumberValidator.php
More file actions
134 lines (113 loc) · 4.16 KB
/
NationalIdentificationNumberValidator.php
File metadata and controls
134 lines (113 loc) · 4.16 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
<?php
namespace Czechphp\NationalIdentificationNumberValidator;
use function array_key_exists;
use function array_merge;
use function date;
use function preg_match;
use function range;
/**
* This validator is not compatible with year 2100 and beyond.
*
* Used resources:
*
* @link https://www.mvcr.cz/clanek/rady-a-sluzby-dokumenty-rodne-cislo.aspx
* @link http://lorenc.info/3MA381/overeni-spravnosti-rodneho-cisla.htm
*/
final class NationalIdentificationNumberValidator
{
public const ERROR_NONE = 0;
public const ERROR_FORMAT = 1;
public const ERROR_MONTH = 2;
public const ERROR_DAY = 3;
public const ERROR_SEQUENCE = 4;
public const ERROR_MODULO = 5;
/**
* Regular expression of expected identification number format
*/
private const REGEXP = '/^(?<year>\d{2})(?<month>\d{2})(?<day>\d{2})\/(?<sequence>\d{3})(?<modulo>\d{1})?$/';
/**
* Female is differentiated by +50 in month field
*/
private const MONTH_FEMALE = 50;
/**
* From year 2004 there are some people with +20 in month field
*/
private const MONTH_AFTER_2004 = 20;
/**
* If modulo field is not empty then whole number should be divisible completely by this value
* and in reverse modulo field should be equal to resulting modulo from division of whole number without modulo field
* except case where modulo is 10, then only last digit is used for modulo (the zero).
*/
private const MODULO = 11;
/**
* @var int
*/
private $currentYear;
public function __construct(int $currentYear = null)
{
$this->currentYear = $currentYear ?: date('y');
}
public function validate(string $value) : int
{
// string has expected format
if (preg_match(self::REGEXP, $value, $matches) !== 1) {
return self::ERROR_FORMAT;
}
$hasModulo = array_key_exists('modulo', $matches) && $matches['modulo'] !== '';
// range of months from 1 to 12
$allowedMonths = array_merge(
range(
1,
12
), // male
range(
1 + self::MONTH_FEMALE,
12 + self::MONTH_FEMALE
) // female
);
// from year 2004 there can be people with +20 in their month number
// without modulo check it would work for people born between 1904 and 19{last_two_digits_of_current_year} too
if ($hasModulo === true && $matches['year'] >= 4 && $matches['year'] <= $this->currentYear) {
$allowedMonths = array_merge(
$allowedMonths,
range(
1 + self::MONTH_AFTER_2004,
12 + self::MONTH_AFTER_2004
), // male
range(
1 + self::MONTH_FEMALE + self::MONTH_AFTER_2004,
12 + self::MONTH_FEMALE + self::MONTH_AFTER_2004
) // female
);
}
if (!in_array($matches['month'], $allowedMonths)) {
return self::ERROR_MONTH;
}
// day is between 1 and 31
if ($matches['day'] < 1 || $matches['day'] > 31) {
return self::ERROR_DAY;
}
// after year 1953 everyone should have modulo
// this validation does not work for people born since year 2000
if ($matches['year'] > 53 && $hasModulo === false) {
return self::ERROR_MODULO;
}
// if there is no modulo then sequence can be between 001 and 999
if ($hasModulo === false && $matches['sequence'] < 1) {
return self::ERROR_SEQUENCE;
}
// number's modulo should be 0
if ($hasModulo === true) {
$number = (int) $matches['year'] . $matches['month'] . $matches['day'] . $matches['sequence'];
$modulo = $number % self::MODULO;
// from year 1954 to 1985 and sometimes even after that, modulo can be 10 which results in 0 as modulo
if ($modulo === 10) {
$modulo = 0;
}
if ($modulo !== ((int) $matches['modulo'])) {
return self::ERROR_MODULO;
}
}
return self::ERROR_NONE;
}
}