Completed
Push — master ( ec0306...353f5c )
by Davide
8s
created

Calculator::calculateSmallString()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 6
ccs 4
cts 4
cp 1
rs 9.4285
cc 1
eloc 4
nc 1
nop 2
crap 1
1
<?php
2
3
namespace CodiceFiscale;
4
5
/**
6
 * Codice Fiscale calculator.
7
 *
8
 * @author davidepastore
9
 */
10
class Calculator
11
{
12
    private $subject;
13
    private $omocodiaLevel = 0;
14
15
    /**
16
     * Array of available vowels.
17
     */
18
    private $vowels = array('A', 'E', 'I', 'O', 'U');
19
20
    /**
21
     * Array of all available months.
22
     */
23
    private $months = array(
24
        '1' => 'A',
25
        '2' => 'B',
26
        '3' => 'C',
27
        '4' => 'D',
28
        '5' => 'E',
29
        '6' => 'H',
30
        '7' => 'L',
31
        '8' => 'M',
32
        '9' => 'P',
33
        '10' => 'R',
34
        '11' => 'S',
35
        '12' => 'T',
36
    );
37
38
    /**
39
     * Array of all avaialable odd characters.
40
     */
41
    private $odd = array(
42
        '0' => 1,
43
        '1' => 0,
44
        '2' => 5,
45
        '3' => 7,
46
        '4' => 9,
47
        '5' => 13,
48
        '6' => 15,
49
        '7' => 17,
50
        '8' => 19,
51
        '9' => 21,
52
        'A' => 1,
53
        'B' => 0,
54
        'C' => 5,
55
        'D' => 7,
56
        'E' => 9,
57
        'F' => 13,
58
        'G' => 15,
59
        'H' => 17,
60
        'I' => 19,
61
        'J' => 21,
62
        'K' => 2,
63
        'L' => 4,
64
        'M' => 18,
65
        'N' => 20,
66
        'O' => 11,
67
        'P' => 3,
68
        'Q' => 6,
69
        'R' => 8,
70
        'S' => 12,
71
        'T' => 14,
72
        'U' => 16,
73
        'V' => 10,
74
        'W' => 22,
75
        'X' => 25,
76
        'Y' => 24,
77
        'Z' => 23,
78
    );
79
80
    /**
81
     * Array of all avaialable even characters.
82
     */
83
    private $even = array(
84
        '0' => 0,
85
        '1' => 1,
86
        '2' => 2,
87
        '3' => 3,
88
        '4' => 4,
89
        '5' => 5,
90
        '6' => 6,
91
        '7' => 7,
92
        '8' => 8,
93
        '9' => 9,
94
        'A' => 0,
95
        'B' => 1,
96
        'C' => 2,
97
        'D' => 3,
98
        'E' => 4,
99
        'F' => 5,
100
        'G' => 6,
101
        'H' => 7,
102
        'I' => 8,
103
        'J' => 9,
104
        'K' => 10,
105
        'L' => 11,
106
        'M' => 12,
107
        'N' => 13,
108
        'O' => 14,
109
        'P' => 15,
110
        'Q' => 16,
111
        'R' => 17,
112
        'S' => 18,
113
        'T' => 19,
114
        'U' => 20,
115
        'V' => 21,
116
        'W' => 22,
117
        'X' => 23,
118
        'Y' => 24,
119
        'Z' => 25,
120
    );
121
122
    /**
123
     * Array of all avaialable omocodia characters.
124
     */
125
    private $omocodiaCodes = array(
126
        '0' => 'L',
127
        '1' => 'M',
128
        '2' => 'N',
129
        '3' => 'P',
130
        '4' => 'Q',
131
        '5' => 'R',
132
        '6' => 'S',
133
        '7' => 'T',
134
        '8' => 'U',
135
        '9' => 'V',
136
    );
137
138
    /**
139
     * Create a Codice Fiscale instance.
140
     *
141
     * @param Subject $subject The subject that will have the codice fiscale.
142
     * @param $properties An array with additional properties.
143
     */
144 20
    public function __construct(Subject $subject, $properties = array())
145
    {
146 20
        $this->subject = $subject;
147
148 20
        if (array_key_exists('omocodiaLevel', $properties)) {
149 18
            $this->omocodiaLevel = $properties['omocodiaLevel'];
150 18
        }
151 20
    }
152
153
    /**
154
     * Calculate the code fiscale.
155
     *
156
     * @returns Returns the complete codice fiscale.
157
     */
158 20
    public function calculate()
159
    {
160 20
        $temporaryCodiceFiscale = $this->calculateSurname().$this->calculateName().
161 20
               $this->calculateBirthDateAndGender().$this->calculateBelfioreCode();
162 20
        $temporaryCodiceFiscale = $this->calculateOmocodia($temporaryCodiceFiscale);
163
164 20
        return $temporaryCodiceFiscale.$this->calculateCheckDigit($temporaryCodiceFiscale);
165
    }
166
167
    /**
168
     * Calculate all possibilities for the code fiscale.
169
     *
170
     * @returns Returns the complete codice fiscale.
171
     */
172 3
    public function calculateAllPossibilities()
173
    {
174 3
        $allPossibilities = array();
175 3
        for ($i = 0; $i < 8; ++$i) {
176 3
            $this->omocodiaLevel = $i;
177 3
            $allPossibilities[] = $this->calculate();
178 3
        }
179
180 3
        return $allPossibilities;
181
    }
182
183
    /**
184
     * Calculate the surname part of the codice fiscale.
185
     *
186
     * @returns Returns the surname part of the codice fiscale.
187
     */
188 20
    private function calculateSurname()
189
    {
190 20
        $consonants = str_replace($this->vowels, '', strtoupper($this->subject->getSurname()));
191 20
        $consonants = preg_replace( '/\s+/', '', $consonants );
192 20
        if (strlen($consonants) > 2) {
193 17
            $result = substr($consonants, 0, 3);
194 17
        } else {
195 3
            $result = $this->calculateSmallString($consonants, $this->subject->getSurname());
196
        }
197
198 20
        return $result;
199
    }
200
201
    /**
202
     * Calculate the name part of the codice fiscale.
203
     *
204
     * @returns Returns the name part of the codice fiscale.
205
     */
206 20
    private function calculateName()
207
    {
208 20
        $consonants = str_replace($this->vowels, '', strtoupper($this->subject->getName()));
209 20
        $consonants = preg_replace( '/\s+/', '', $consonants );
210 20
        if (strlen($consonants) > 3) {
211 6
            $result = $consonants[0].$consonants[2].$consonants[3];
212 20
        } elseif (strlen($consonants) == 3) {
213 1
            $result = $consonants;
214 1
        } else {
215 13
            $result = $this->calculateSmallString($consonants, $this->subject->getName());
216
        }
217
218 20
        return $result;
219
    }
220
    
221
    /**
222
     * Calculate small string for the given parameters (used by name and surname).
223
     * @param $consonants A consonants string.
224
     * @param $string The small string.
225
     * @returns Returns the calculated result for the small string.
226
     */
227 13
    private function calculateSmallString($consonants, $string)
228
    {
229 13
        $vowels = str_replace(str_split($consonants), '', strtoupper($string));
230 13
        $result = substr($consonants.$vowels.'XXX', 0, 3);
231 13
        return $result;
232
    }
233
234
    /**
235
     * Calculate the birth date and the gender.
236
     *
237
     * @returns Returns the birth date and gender part of the codice fiscale.
238
     */
239 20
    private function calculateBirthDateAndGender()
240
    {
241 20
        $year = $this->subject->getBirthDate()->format('y');
242 20
        $month = $this->months[$this->subject->getBirthDate()->format('n')];
243 20
        $day = $this->subject->getBirthDate()->format('d');
244 20
        if (strtoupper($this->subject->getGender()) == 'F') {
245 2
            $day += 40;
246 2
        }
247
248 20
        return $year.$month.$day;
249
    }
250
251
    /**
252
     * Calculate the Belfiore code.
253
     *
254
     * @returns Returns the Belfiore code.
255
     */
256 20
    private function calculateBelfioreCode()
257
    {
258 20
        return strtoupper($this->subject->getBelfioreCode());
259
    }
260
261
    /**
262
     * Calculate the check digit.
263
     *
264
     * @param $temporaryCodiceFiscale The first part of the codice fiscale.
265
     * @returns Returns the check digit part of the codice fiscale.
266
     */
267 20
    private function calculateCheckDigit($temporaryCodiceFiscale)
268
    {
269 20
        $sumEven = $this->calculateSumByDictionary($temporaryCodiceFiscale, $this->even, 1);
270 20
        $sumOdd = $this->calculateSumByDictionary($temporaryCodiceFiscale, $this->odd, 0);
271
272 20
        return chr(($sumOdd + $sumEven) % 26 + 65);
273
    }
274
275
    /**
276
     * Calculate the sum by the given dictionary for the given temporary codice fiscale.
277
     *
278
     * @param $temporaryCodiceFiscale The temporary codice fiscale.
279
     * @param $dictionaryArray The dictionary array.
280
     * @param $i The start index value.
281
     * @returns Returns the sum by the given dictionary for the given temporary codice fiscale.
282
     */
283 20
    private function calculateSumByDictionary($temporaryCodiceFiscale, $dictionaryArray, $i)
284
    {
285 20
        $sum = 0;
286 20
        for (; $i < 15; $i = $i + 2) {
287 20
            $k = $temporaryCodiceFiscale{$i};
288 20
            $sum = $sum + $dictionaryArray[$k];
289 20
        }
290
291 20
        return $sum;
292
    }
293
294
    /**
295
     * Calculate the omocodia case (additional translation).
296
     *
297
     * @param $temporaryCodiceFiscale The first part of the codice fiscale.
298
     * @returns Returns the new codice fiscale.
299
     */
300 20
    private function calculateOmocodia($temporaryCodiceFiscale)
301
    {
302 20
        if ($this->omocodiaLevel > 0) {
303 7
            $omocodiaLevelApplied = 0;
304 7
            for ($i = strlen($temporaryCodiceFiscale) - 1; $i > 0; --$i) {
305 7
                $k = $temporaryCodiceFiscale{$i};
306 7
                if ($omocodiaLevelApplied < $this->omocodiaLevel && is_numeric($k)) {
307 7
                    $newChar = $this->omocodiaCodes[$k];
308 7
                    $temporaryCodiceFiscale{$i}
309
                    = $newChar;
310 7
                    ++$omocodiaLevelApplied;
311 7
                }
312 7
            }
313 7
        }
314
315 20
        return $temporaryCodiceFiscale;
316
    }
317
}
318