Completed
Pull Request — master (#39)
by Davide
01:20
created

Validator::getError()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 1
1
<?php
2
3
namespace CodiceFiscale;
4
5
use DateTime;
6
use Exception;
7
8
/**
9
 * Description of Validator
10
 *
11
 * @author Antonio Turdo <[email protected]>
12
 * @author davidepastore
13
 */
14
class Validator extends AbstractCalculator
15
{
16
    private $regexs = array(
17
        0 => '/^[a-z]{6}[0-9]{2}[a-z][0-9]{2}[a-z][0-9]{3}[a-z]$/i', //RSSMRA85T10A562S
18
        1 => '/^[a-z]{6}[0-9]{2}[a-z][0-9]{2}[a-z][0-9]{2}[a-z]{2}$/i', //RSSMRA85T10A56NH
19
        2 => '/^[a-z]{6}[0-9]{2}[a-z][0-9]{2}[a-z][0-9][a-z][0-9][a-z]$/i', //RSSMRA85T10A5S2E
20
        3 => '/^[a-z]{6}[0-9]{2}[a-z][0-9]{2}[a-z][0-9][a-z]{3}$/i', //RSSMRA85T10A5SNT
21
        4 => '/^[a-z]{6}[0-9]{2}[a-z][0-9]{2}[a-z]{2}[0-9]{2}[a-z]$/i', //RSSMRA85T10AR62N
22
        5 => '/^[a-z]{6}[0-9]{2}[a-z][0-9]{2}[a-z]{2}[0-9][a-z]{2}$/i', //RSSMRA85T10AR6NC
23
        6 => '/^[a-z]{6}[0-9]{2}[a-z][0-9]{2}[a-z]{3}[0-9][a-z]$/i', //RSSMRA85T10ARS2Z
24
        7 => '/^[a-z]{6}[0-9]{2}[a-z][0-9]{2}[a-z]{5}$/i', //RSSMRA85T10ARSNO
25
        8 => '/^[a-z]{6}[0-9]{2}[a-z][0-9][a-z]{2}[0-9]{3}[a-z]$/i', //RSSMRA85T1LA562V
26
        9 => '/^[a-z]{6}[0-9]{2}[a-z][0-9][a-z]{2}[0-9]{2}[a-z]{2}$/i', //RSSMRA85T1LA56NK
27
        10 => '/^[a-z]{6}[0-9]{2}[a-z][0-9][a-z]{2}[0-9][a-z][0-9][a-z]$/i', //RSSMRA85T1LA5S2H
28
        11 => '/^[a-z]{6}[0-9]{2}[a-z][0-9][a-z]{2}[0-9][a-z]{3}$/i', //RSSMRA85T1LA5SNW
29
        12 => '/^[a-z]{6}[0-9]{2}[a-z][0-9][a-z]{3}[0-9]{2}[a-z]$/i', //RSSMRA85T1LAR62Q
30
        13 => '/^[a-z]{6}[0-9]{2}[a-z][0-9][a-z]{3}[0-9][a-z]{2}$/i', //RSSMRA85T1LAR6NF
31
        14 => '/^[a-z]{6}[0-9]{2}[a-z][0-9][a-z]{4}[0-9][a-z]$/i', //RSSMRA85T1LARS2C
32
        15 => '/^[a-z]{6}[0-9]{2}[a-z][0-9][a-z]{6}$/i', //RSSMRA85T1LARSNR
33
        16 => '/^[a-z]{6}[0-9]{2}[a-z]{2}[0-9][a-z][0-9]{3}[a-z]$/i', //RSSMRA85TM0A562D
34
        17 => '/^[a-z]{6}[0-9]{2}[a-z]{2}[0-9][a-z][0-9]{2}[a-z]{2}$/i', //RSSMRA85TM0A56NS
35
        18 => '/^[a-z]{6}[0-9]{2}[a-z]{2}[0-9][a-z][0-9][a-z][0-9][a-z]$/i', //RSSMRA85TM0A5S2P
36
        19 => '/^[a-z]{6}[0-9]{2}[a-z]{2}[0-9][a-z][0-9][a-z]{3}$/i', //RSSMRA85TM0A5SNE
37
        20 => '/^[a-z]{6}[0-9]{2}[a-z]{2}[0-9][a-z]{2}[0-9]{2}[a-z]$/i', //RSSMRA85TM0AR62Y
38
        21 => '/^[a-z]{6}[0-9]{2}[a-z]{2}[0-9][a-z]{2}[0-9][a-z]{2}$/i', //RSSMRA85TM0AR6NN
39
        22 => '/^[a-z]{6}[0-9]{2}[a-z]{2}[0-9][a-z]{3}[0-9][a-z]$/i', //RSSMRA85TM0ARS2K
40
        23 => '/^[a-z]{6}[0-9]{2}[a-z]{2}[0-9][a-z]{5}$/i', //RSSMRA85TM0ARSNZ
41
        24 => '/^[a-z]{6}[0-9]{2}[a-z]{4}[0-9]{3}[a-z]$/i', //RSSMRA85TMLA562G
42
        25 => '/^[a-z]{6}[0-9]{2}[a-z]{4}[0-9]{2}[a-z]{2}$/i', //RSSMRA85TMLA56NV
43
        26 => '/^[a-z]{6}[0-9]{2}[a-z]{4}[0-9][a-z][0-9][a-z]$/i', //RSSMRA85TMLA5S2S
44
        27 => '/^[a-z]{6}[0-9]{2}[a-z]{4}[0-9][a-z]{3}$/i', //RSSMRA85TMLA5SNH
45
        28 => '/^[a-z]{6}[0-9]{2}[a-z]{5}[0-9]{2}[a-z]$/i', //RSSMRA85TMLAR62B
46
        29 => '/^[a-z]{6}[0-9]{2}[a-z]{5}[0-9][a-z]{2}$/i', //RSSMRA85TMLAR6NQ
47
        30 => '/^[a-z]{6}[0-9]{2}[a-z]{6}[0-9][a-z]$/i', //RSSMRA85TMLARS2N
48
        31 => '/^[a-z]{6}[0-9]{2}[a-z]{8}$/i', //RSSMRA85TMLARSNC
49
        32 => '/^[a-z]{6}[0-9][a-z]{2}[0-9]{2}[a-z][0-9]{3}[a-z]$/i', //RSSMRA8RT10A562E
50
        33 => '/^[a-z]{6}[0-9][a-z]{2}[0-9]{2}[a-z][0-9]{2}[a-z]{2}$/i', //RSSMRA8RT10A56NT
51
        34 => '/^[a-z]{6}[0-9][a-z]{2}[0-9]{2}[a-z][0-9][a-z][0-9][a-z]$/i', //RSSMRA8RT10A5S2Q
52
        35 => '/^[a-z]{6}[0-9][a-z]{2}[0-9]{2}[a-z][0-9][a-z]{3}$/i', //RSSMRA8RT10A5SNF
53
        36 => '/^[a-z]{6}[0-9][a-z]{2}[0-9]{2}[a-z]{2}[0-9]{2}[a-z]$/i', //RSSMRA8RT10AR62Z
54
        37 => '/^[a-z]{6}[0-9][a-z]{2}[0-9]{2}[a-z]{2}[0-9][a-z]{2}$/i', //RSSMRA8RT10AR6NO
55
        38 => '/^[a-z]{6}[0-9][a-z]{2}[0-9]{2}[a-z]{3}[0-9][a-z]$/i', //RSSMRA8RT10ARS2L
56
        39 => '/^[a-z]{6}[0-9][a-z]{2}[0-9]{2}[a-z]{5}$/i', //RSSMRA8RT10ARSNA
57
        40 => '/^[a-z]{6}[0-9][a-z]{2}[0-9][a-z]{2}[0-9]{3}[a-z]$/i', //RSSMRA8RT1LA562H
58
        41 => '/^[a-z]{6}[0-9][a-z]{2}[0-9][a-z]{2}[0-9]{2}[a-z]{2}$/i', //RSSMRA8RT1LA56NW
59
        42 => '/^[a-z]{6}[0-9][a-z]{2}[0-9][a-z]{2}[0-9][a-z][0-9][a-z]$/i', //RSSMRA8RT1LA5S2T
60
        43 => '/^[a-z]{6}[0-9][a-z]{2}[0-9][a-z]{2}[0-9][a-z]{3}$/i', //RSSMRA8RT1LA5SNI
61
        44 => '/^[a-z]{6}[0-9][a-z]{2}[0-9][a-z]{3}[0-9]{2}[a-z]$/i', //RSSMRA8RT1LAR62C
62
        45 => '/^[a-z]{6}[0-9][a-z]{2}[0-9][a-z]{3}[0-9][a-z]{2}$/i', //RSSMRA8RT1LAR6NR
63
        46 => '/^[a-z]{6}[0-9][a-z]{2}[0-9][a-z]{4}[0-9][a-z]$/i', //RSSMRA8RT1LARS2O
64
        47 => '/^[a-z]{6}[0-9][a-z]{2}[0-9][a-z]{6}$/i', //RSSMRA8RT1LARSND
65
        48 => '/^[a-z]{6}[0-9][a-z]{3}[0-9][a-z][0-9]{3}[a-z]$/i', //RSSMRA8RTM0A562P
66
        49 => '/^[a-z]{6}[0-9][a-z]{3}[0-9][a-z][0-9]{2}[a-z]{2}$/i', //RSSMRA8RTM0A56NE
67
        50 => '/^[a-z]{6}[0-9][a-z]{3}[0-9][a-z][0-9][a-z][0-9][a-z]$/i', //RSSMRA8RTM0A5S2B
68
        51 => '/^[a-z]{6}[0-9][a-z]{3}[0-9][a-z][0-9][a-z]{3}$/i', //RSSMRA8RTM0A5SNQ
69
        52 => '/^[a-z]{6}[0-9][a-z]{3}[0-9][a-z]{2}[0-9]{2}[a-z]$/i', //RSSMRA8RTM0AR62K
70
        53 => '/^[a-z]{6}[0-9][a-z]{3}[0-9][a-z]{2}[0-9][a-z]{2}$/i', //RSSMRA8RTM0AR6NZ
71
        54 => '/^[a-z]{6}[0-9][a-z]{3}[0-9][a-z]{3}[0-9][a-z]$/i', //RSSMRA8RTM0ARS2W
72
        55 => '/^[a-z]{6}[0-9][a-z]{3}[0-9][a-z]{5}$/i', //RSSMRA8RTM0ARSNL
73
        56 => '/^[a-z]{6}[0-9][a-z]{5}[0-9]{3}[a-z]$/i', //RSSMRA8RTMLA562S
74
        57 => '/^[a-z]{6}[0-9][a-z]{5}[0-9]{2}[a-z]{2}$/i', //RSSMRA8RTMLA56NH
75
        58 => '/^[a-z]{6}[0-9][a-z]{5}[0-9][a-z][0-9][a-z]$/i', //RSSMRA8RTMLA5S2E
76
        59 => '/^[a-z]{6}[0-9][a-z]{5}[0-9][a-z]{3}$/i', //RSSMRA8RTMLA5SNT
77
        60 => '/^[a-z]{6}[0-9][a-z]{6}[0-9]{2}[a-z]$/i', //RSSMRA8RTMLAR62N
78
        61 => '/^[a-z]{6}[0-9][a-z]{6}[0-9][a-z]{2}$/i', //RSSMRA8RTMLAR6NC
79
        62 => '/^[a-z]{6}[0-9][a-z]{7}[0-9][a-z]$/i', //RSSMRA8RTMLARS2Z
80
        63 => '/^[a-z]{6}[0-9][a-z]{9}$/i', //RSSMRA8RTMLARSNO
81
        64 => '/^[a-z]{7}[0-9][a-z][0-9]{2}[a-z][0-9]{3}[a-z]$/i', //RSSMRAU5T10A562P
82
        65 => '/^[a-z]{7}[0-9][a-z][0-9]{2}[a-z][0-9]{2}[a-z]{2}$/i', //RSSMRAU5T10A56NE
83
        66 => '/^[a-z]{7}[0-9][a-z][0-9]{2}[a-z][0-9][a-z][0-9][a-z]$/i', //RSSMRAU5T10A5S2B
84
        67 => '/^[a-z]{7}[0-9][a-z][0-9]{2}[a-z][0-9][a-z]{3}$/i', //RSSMRAU5T10A5SNQ
85
        68 => '/^[a-z]{7}[0-9][a-z][0-9]{2}[a-z]{2}[0-9]{2}[a-z]$/i', //RSSMRAU5T10AR62K
86
        69 => '/^[a-z]{7}[0-9][a-z][0-9]{2}[a-z]{2}[0-9][a-z]{2}$/i', //RSSMRAU5T10AR6NZ
87
        70 => '/^[a-z]{7}[0-9][a-z][0-9]{2}[a-z]{3}[0-9][a-z]$/i', //RSSMRAU5T10ARS2W
88
        71 => '/^[a-z]{7}[0-9][a-z][0-9]{2}[a-z]{5}$/i', //RSSMRAU5T10ARSNL
89
        72 => '/^[a-z]{7}[0-9][a-z][0-9][a-z]{2}[0-9]{3}[a-z]$/i', //RSSMRAU5T1LA562S
90
        73 => '/^[a-z]{7}[0-9][a-z][0-9][a-z]{2}[0-9]{2}[a-z]{2}$/i', //RSSMRAU5T1LA56NH
91
        74 => '/^[a-z]{7}[0-9][a-z][0-9][a-z]{2}[0-9][a-z][0-9][a-z]$/i', //RSSMRAU5T1LA5S2E
92
        75 => '/^[a-z]{7}[0-9][a-z][0-9][a-z]{2}[0-9][a-z]{3}$/i', //RSSMRAU5T1LA5SNT
93
        76 => '/^[a-z]{7}[0-9][a-z][0-9][a-z]{3}[0-9]{2}[a-z]$/i', //RSSMRAU5T1LAR62N
94
        77 => '/^[a-z]{7}[0-9][a-z][0-9][a-z]{3}[0-9][a-z]{2}$/i', //RSSMRAU5T1LAR6NC
95
        78 => '/^[a-z]{7}[0-9][a-z][0-9][a-z]{4}[0-9][a-z]$/i', //RSSMRAU5T1LARS2Z
96
        79 => '/^[a-z]{7}[0-9][a-z][0-9][a-z]{6}$/i', //RSSMRAU5T1LARSNO
97
        80 => '/^[a-z]{7}[0-9][a-z]{2}[0-9][a-z][0-9]{3}[a-z]$/i', //RSSMRAU5TM0A562A
98
        81 => '/^[a-z]{7}[0-9][a-z]{2}[0-9][a-z][0-9]{2}[a-z]{2}$/i', //RSSMRAU5TM0A56NP
99
        82 => '/^[a-z]{7}[0-9][a-z]{2}[0-9][a-z][0-9][a-z][0-9][a-z]$/i', //RSSMRAU5TM0A5S2M
100
        83 => '/^[a-z]{7}[0-9][a-z]{2}[0-9][a-z][0-9][a-z]{3}$/i', //RSSMRAU5TM0A5SNB
101
        84 => '/^[a-z]{7}[0-9][a-z]{2}[0-9][a-z]{2}[0-9]{2}[a-z]$/i', //RSSMRAU5TM0AR62V
102
        85 => '/^[a-z]{7}[0-9][a-z]{2}[0-9][a-z]{2}[0-9][a-z]{2}$/i', //RSSMRAU5TM0AR6NK
103
        86 => '/^[a-z]{7}[0-9][a-z]{2}[0-9][a-z]{3}[0-9][a-z]$/i', //RSSMRAU5TM0ARS2H
104
        87 => '/^[a-z]{7}[0-9][a-z]{2}[0-9][a-z]{5}$/i', //RSSMRAU5TM0ARSNW
105
        88 => '/^[a-z]{7}[0-9][a-z]{4}[0-9]{3}[a-z]$/i', //RSSMRAU5TMLA562D
106
        89 => '/^[a-z]{7}[0-9][a-z]{4}[0-9]{2}[a-z]{2}$/i', //RSSMRAU5TMLA56NS
107
        90 => '/^[a-z]{7}[0-9][a-z]{4}[0-9][a-z][0-9][a-z]$/i', //RSSMRAU5TMLA5S2P
108
        91 => '/^[a-z]{7}[0-9][a-z]{4}[0-9][a-z]{3}$/i', //RSSMRAU5TMLA5SNE
109
        92 => '/^[a-z]{7}[0-9][a-z]{5}[0-9]{2}[a-z]$/i', //RSSMRAU5TMLAR62Y
110
        93 => '/^[a-z]{7}[0-9][a-z]{5}[0-9][a-z]{2}$/i', //RSSMRAU5TMLAR6NN
111
        94 => '/^[a-z]{7}[0-9][a-z]{6}[0-9][a-z]$/i', //RSSMRAU5TMLARS2K
112
        95 => '/^[a-z]{7}[0-9][a-z]{8}$/i', //RSSMRAU5TMLARSNZ
113
        96 => '/^[a-z]{9}[0-9]{2}[a-z][0-9]{3}[a-z]$/i', //RSSMRAURT10A562B
114
        97 => '/^[a-z]{9}[0-9]{2}[a-z][0-9]{2}[a-z]{2}$/i', //RSSMRAURT10A56NQ
115
        98 => '/^[a-z]{9}[0-9]{2}[a-z][0-9][a-z][0-9][a-z]$/i', //RSSMRAURT10A5S2N
116
        99 => '/^[a-z]{9}[0-9]{2}[a-z][0-9][a-z]{3}$/i', //RSSMRAURT10A5SNC
117
        100 => '/^[a-z]{9}[0-9]{2}[a-z]{2}[0-9]{2}[a-z]$/i', //RSSMRAURT10AR62W
118
        101 => '/^[a-z]{9}[0-9]{2}[a-z]{2}[0-9][a-z]{2}$/i', //RSSMRAURT10AR6NL
119
        102 => '/^[a-z]{9}[0-9]{2}[a-z]{3}[0-9][a-z]$/i', //RSSMRAURT10ARS2I
120
        103 => '/^[a-z]{9}[0-9]{2}[a-z]{5}$/i', //RSSMRAURT10ARSNX
121
        104 => '/^[a-z]{9}[0-9][a-z]{2}[0-9]{3}[a-z]$/i', //RSSMRAURT1LA562E
122
        105 => '/^[a-z]{9}[0-9][a-z]{2}[0-9]{2}[a-z]{2}$/i', //RSSMRAURT1LA56NT
123
        106 => '/^[a-z]{9}[0-9][a-z]{2}[0-9][a-z][0-9][a-z]$/i', //RSSMRAURT1LA5S2Q
124
        107 => '/^[a-z]{9}[0-9][a-z]{2}[0-9][a-z]{3}$/i', //RSSMRAURT1LA5SNF
125
        108 => '/^[a-z]{9}[0-9][a-z]{3}[0-9]{2}[a-z]$/i', //RSSMRAURT1LAR62Z
126
        109 => '/^[a-z]{9}[0-9][a-z]{3}[0-9][a-z]{2}$/i', //RSSMRAURT1LAR6NO
127
        110 => '/^[a-z]{9}[0-9][a-z]{4}[0-9][a-z]$/i', //RSSMRAURT1LARS2L
128
        111 => '/^[a-z]{9}[0-9][a-z]{6}$/i', //RSSMRAURT1LARSNA
129
        112 => '/^[a-z]{10}[0-9][a-z][0-9]{3}[a-z]$/i', //RSSMRAURTM0A562M
130
        113 => '/^[a-z]{10}[0-9][a-z][0-9]{2}[a-z]{2}$/i', //RSSMRAURTM0A56NB
131
        114 => '/^[a-z]{10}[0-9][a-z][0-9][a-z][0-9][a-z]$/i', //RSSMRAURTM0A5S2Y
132
        115 => '/^[a-z]{10}[0-9][a-z][0-9][a-z]{3}$/i', //RSSMRAURTM0A5SNN
133
        116 => '/^[a-z]{10}[0-9][a-z]{2}[0-9]{2}[a-z]$/i', //RSSMRAURTM0AR62H
134
        117 => '/^[a-z]{10}[0-9][a-z]{2}[0-9][a-z]{2}$/i', //RSSMRAURTM0AR6NW
135
        118 => '/^[a-z]{10}[0-9][a-z]{3}[0-9][a-z]$/i', //RSSMRAURTM0ARS2T
136
        119 => '/^[a-z]{10}[0-9][a-z]{5}$/i', //RSSMRAURTM0ARSNI
137
        120 => '/^[a-z]{12}[0-9]{3}[a-z]$/i', //RSSMRAURTMLA562P
138
        121 => '/^[a-z]{12}[0-9]{2}[a-z]{2}$/i', //RSSMRAURTMLA56NE
139
        122 => '/^[a-z]{12}[0-9][a-z][0-9][a-z]$/i', //RSSMRAURTMLA5S2B
140
        123 => '/^[a-z]{12}[0-9][a-z]{3}$/i', //RSSMRAURTMLA5SNQ
141
        124 => '/^[a-z]{13}[0-9]{2}[a-z]$/i', //RSSMRAURTMLAR62K
142
        125 => '/^[a-z]{13}[0-9][a-z]{2}$/i', //RSSMRAURTMLAR6NZ
143
        126 => '/^[a-z]{14}[0-9][a-z]$/i', //RSSMRAURTMLARS2W
144
        127 => '/^[a-z]{16}$/i', //RSSMRAURTMLARSNL
145
    );
146
    
147
    private $codiceFiscale;
148
    private $omocodiaAllowed = true;
149
    private $century = null;
150
    
151
    private $foundOmocodiaLevel = null;
152
    private $codiceFiscaleWithoutOmocodia = null;
153
    private $birthDate = null;
154
    private $gender = null;
155
    
156
    private $error = null;
157
    private $isValid = false;
158
159
    /**
160
     * Create a Validator instance.
161
     *
162
     * @param string $codiceFiscale the codice fiscale to validate
163
     * @param array $properties  An array with additional properties.
164
     */
165 154
    public function __construct($codiceFiscale, $properties = array())
166
    {
167 154
        $this->codiceFiscale = strtoupper($codiceFiscale);
168
        
169 154
        if (array_key_exists('omocodiaAllowed', $properties)) {
170 154
            $this->omocodiaAllowed = $properties['omocodiaAllowed'];
171
        }
172
        
173 154
        if (array_key_exists('century', $properties)) {
174 143
            $this->century = $properties['century'];
175
        }
176
        
177
        try {
178 154
            $this->validateLength();
179
180 152
            $this->validateFormat();
181
182 151
            $this->validateCheckDigit();
183
            
184 150
            $this->validateAndReplaceOmocodia();
185
186 149
            $this->validateBirthDateAndGender();
187
188 146
            $this->isValid = true;
189 8
        } catch (Exception $e) {
190 8
            $this->error = $e->getMessage();
191
        }
192 154
    }
193
194
    /**
195
     * Validates length
196
     *
197
     * @throws Exception
198
     */
199 154
    private function validateLength()
200
    {
201
        // check empty
202 154
        if (empty($this->codiceFiscale)) {
203 1
            throw new Exception('The codice fiscale to validate is empty');
204
        }
205
206
        // check length
207 153
        if (strlen($this->codiceFiscale) !== 16) {
208 1
            throw new Exception('The codice fiscale to validate has an invalid length');
209
        }
210 152
    }
211
212
    /**
213
     * Validates format
214
     *
215
     * @throws Exception
216
     */
217 152
    private function validateFormat()
218
    {
219 152
        $regexpValid = false;
220 152
        if (!$this->omocodiaAllowed) {
221
            // just one regex
222 1
            if (preg_match($this->regexs[0], $this->codiceFiscale)) {
223 1
                $this->foundOmocodiaLevel = 0;
224 1
                $regexpValid = true;
225
            }
226
        } else {
227
            // all the regex
228 151
            $omocodiaLevelApplied = 0;
229 151
            foreach ($this->regexs as $regex) {
230 151
                if (preg_match($regex, $this->codiceFiscale)) {
231 150
                    $this->foundOmocodiaLevel = $omocodiaLevelApplied;
232 150
                    $regexpValid = true;
233 150
                    break;
234
                }
235 133
                $omocodiaLevelApplied++;
236
            }
237
        }
238
239 152
        if (!$regexpValid) {
240 1
            throw new Exception('The codice fiscale to validate has an invalid format');
241
        }
242 151
    }
243
    
244
    /**
245
     * Validates check digit
246
     *
247
     * @throws Exception
248
     */
249 151
    private function validateCheckDigit()
250
    {
251 151
        $checkDigit = $this->calculateCheckDigit($this->codiceFiscale);
252 151
        if ($checkDigit != $this->codiceFiscale[15]) {
253 1
            throw new Exception('The codice fiscale to validate has an invalid control character');
254
        }
255 150
    }
256
    
257
    /**
258
     * Validates omocodia and replace with matching chars
259
     *
260
     * @throws Exception
261
     */
262 150
    private function validateAndReplaceOmocodia()
263
    {
264
        // check and replace omocodie
265 150
        $this->codiceFiscaleWithoutOmocodia = $this->codiceFiscale;
266 150
        $this->replaceOmocodiaSection(2, 1, 1, $this->omocodiaPositions[0]);
267 149
        $this->replaceOmocodiaSection(4, 2, 3, $this->omocodiaPositions[1]);
268 149
        $this->replaceOmocodiaSection(8, 4, 7, $this->omocodiaPositions[2]);
269 149
        $this->replaceOmocodiaSection(16, 8, 15, $this->omocodiaPositions[3]);
270 149
        $this->replaceOmocodiaSection(32, 16, 31, $this->omocodiaPositions[4]);
271 149
        $this->replaceOmocodiaSection(64, 32, 63, $this->omocodiaPositions[5]);
272 149
        $this->replaceOmocodiaSection(128, 64, 127, $this->omocodiaPositions[6]);
273 149
    }
274
275
    /**
276
     * Replace a section of the omocodia.
277
     *
278
     * @param int $divider The divider.
279
     * @param int $startingIndex The starting index.
280
     * @param int $endingIndex The ending index.
281
     * @param int $characterIndex The index to use to make the substitutions on the $codiceFiscaleWithoutOmocodia.
282
     * @throws Exception
283
     */
284 150
    private function replaceOmocodiaSection($divider, $startingIndex, $endingIndex, $characterIndex)
285
    {
286 150
        if ($this->foundOmocodiaLevel % $divider >= $startingIndex && $this->foundOmocodiaLevel % $divider <= $endingIndex) {
287 132
            $charToCheck = $this->codiceFiscaleWithoutOmocodia[$characterIndex];
288 132
            if (!in_array($charToCheck, $this->omocodiaCodes)) {
289 1
                throw new Exception('The codice fiscale to validate has an invalid character');
290
            }
291 131
            $newChar = array_search($charToCheck, $this->omocodiaCodes);
292 131
            $this->codiceFiscaleWithoutOmocodia[$characterIndex] = $newChar;
293
        }
294 149
    }
295
    
296
    /**
297
     * Validates birthdate and gender
298
     *
299
     * @throws Exception
300
     */
301 149
    private function validateBirthDateAndGender()
302
    {
303
        // calculate day and sex
304 149
        $day = (int) substr($this->codiceFiscaleWithoutOmocodia, 9, 2);
305 149
        $this->gender = $day > 40 ? self::CHR_WOMEN : self::CHR_MALE;
306
307 149
        if ($this->gender === self::CHR_WOMEN) {
308 3
            $day -= 40;
309
        }
310
311
        // check day
312 149
        if ($day > 31) {
313 1
            throw new Exception('The codice fiscale to validate has invalid characters for birth day');
314
        }
315
316
        // check month
317 148
        $monthChar = substr($this->codiceFiscaleWithoutOmocodia, 8, 1);
318 148
        if (!in_array($monthChar, $this->months)) {
319 1
            throw new Exception('The codice fiscale to validate has an invalid character for birth month');
320
        }
321
        
322
        // calculate month, year and century
323 147
        $month = array_search($monthChar, $this->months);
324 147
        $year = substr($this->codiceFiscaleWithoutOmocodia, 6, 2);
325 147
        $century = $this->calculateCentury($year);
326
327
        // validate and calculate birth date
328 147
        if (!checkdate($month, $day, $century.$year)) {
329 1
            throw new Exception('The codice fiscale to validate has an non existent birth date');
330
        }
331
        
332 146
        $this->birthDate = new DateTime();
333 146
        $this->birthDate->setDate($century.$year, $month, $day)->setTime(0, 0, 0)->format('Y-m-d');
334 146
    }
335
336
    /**
337
     *
338
     * @param string $year
339
     * @return string
340
     * @throws Exception
341
     */
342 147
    private function calculateCentury($year)
343
    {
344 147
        $currentDate = new DateTime();
345 147
        $currentYear = $currentDate->format('y');
346 147
        if (!is_null($this->century)) {
347 1
            $century = $this->century;
348
        } else {
349 146
            $currentCentury = substr($currentDate->format('Y'), 0, 2);
350 146
            $century = $year < $currentYear ? $currentCentury : $currentCentury - 1;
351
        }
352
        
353 147
        return $century;
354
    }
355
356
    /**
357
     * Return the validation error
358
     *
359
     * @return string
360
     */
361 11
    public function getError()
362
    {
363 11
        return $this->error;
364
    }
365
366
    /**
367
     * Return true if the provided codice fiscale is valid, false otherwise
368
     *
369
     * @return boolean
370
     */
371 154
    public function isFormallyValid()
372
    {
373 154
        return $this->isValid;
374
    }
375
376
    /**
377
     * Return true if the provided codice fiscale is an omocodia, false otherwise
378
     *
379
     * @return boolean
380
     */
381 1
    public function isOmocodia()
382
    {
383 1
        return $this->foundOmocodiaLevel > 0;
384
    }
385
386
    /**
387
     * Return the provided codice fiscale, cleaned up by omocodia
388
     *
389
     * @return string
390
     */
391 143
    protected function getCodiceFiscaleWithoutOmocodia()
392
    {
393 143
        return $this->codiceFiscaleWithoutOmocodia;
394
    }
395
396
    /**
397
     * Return the birth date
398
     *
399
     * @return DateTime
400
     */
401 143
    protected function getBirthDate()
402
    {
403 143
        return $this->birthDate;
404
    }
405
406
    /**
407
     * Return the gender
408
     *
409
     * @return string
410
     */
411 143
    protected function getGender()
412
    {
413 143
        return $this->gender;
414
    }
415
}
416