Completed
Push — master ( 5b0fa8...e97694 )
by Davide
02:10
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 1 Features 0
Metric Value
c 1
b 1
f 0
dl 0
loc 6
ccs 4
cts 4
cp 1
rs 9.4286
cc 1
eloc 4
nc 1
nop 2
crap 1
1
<?php
2
3
namespace DavidePastore\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 17
    public function __construct(Subject $subject, $properties = array())
145
    {
146 17
        $this->subject = $subject;
147
148 17
        if (array_key_exists('omocodiaLevel', $properties)) {
149 15
            $this->omocodiaLevel = $properties['omocodiaLevel'];
150 15
        }
151 17
    }
152
153
    /**
154
     * Calculate the code fiscale.
155
     *
156
     * @returns Returns the complete codice fiscale.
157
     */
158 17
    public function calculate()
159
    {
160 17
        $temporaryCodiceFiscale = $this->calculateSurname().$this->calculateName().
161 17
               $this->calculateBirthDateAndGender().$this->calculateBelfioreCode();
162 17
        $temporaryCodiceFiscale = $this->calculateOmocodia($temporaryCodiceFiscale);
163
164 17
        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 17
    private function calculateSurname()
189
    {
190 17
        $consonants = str_replace($this->vowels, '', strtoupper($this->subject->getSurname()));
191 17
        if (strlen($consonants) > 2) {
192 14
            $result = substr($consonants, 0, 3);
193 14
        } else {
194 3
            $result = $this->calculateSmallString($consonants, $this->subject->getSurname());
195
        }
196
197 17
        return $result;
198
    }
199
200
    /**
201
     * Calculate the name part of the codice fiscale.
202
     *
203
     * @returns Returns the name part of the codice fiscale.
204
     */
205 17
    private function calculateName()
206
    {
207 17
        $consonants = str_replace($this->vowels, '', strtoupper($this->subject->getName()));
208 17
        if (strlen($consonants) > 3) {
209 5
            $result = $consonants[0].$consonants[2].$consonants[3];
210 17
        } elseif (strlen($consonants) == 3) {
211
            $result = implode($consonants);
212
        } else {
213 12
            $result = $this->calculateSmallString($consonants, $this->subject->getName());
214
        }
215
216 17
        return $result;
217
    }
218
    
219
    /**
220
     * Calculate small string for the given parameters (used by name and surname).
221
     * @param $consonants A consonants string.
222
     * @param $string The small string.
223
     * @returns Returns the calculated result for the small string.
224
     */
225 12
    private function calculateSmallString($consonants, $string)
226
    {
227 12
        $vowels = str_replace(str_split($consonants), '', strtoupper($string));
228 12
        $result = substr($consonants.$vowels.'XXX', 0, 3);
229 12
        return $result;
230
    }
231
232
    /**
233
     * Calculate the birth date and the gender.
234
     *
235
     * @returns Returns the birth date and gender part of the codice fiscale.
236
     */
237 17
    private function calculateBirthDateAndGender()
238
    {
239 17
        $year = $this->subject->getBirthDate()->format('y');
240 17
        $month = $this->months[$this->subject->getBirthDate()->format('n')];
241 17
        $day = $this->subject->getBirthDate()->format('d');
242 17
        if (strtoupper($this->subject->getGender()) == 'F') {
243 1
            $day += 40;
244 1
        }
245
246 17
        return $year.$month.$day;
247
    }
248
249
    /**
250
     * Calculate the Belfiore code.
251
     *
252
     * @returns Returns the Belfiore code.
253
     */
254 17
    private function calculateBelfioreCode()
255
    {
256 17
        return strtoupper($this->subject->getBelfioreCode());
257
    }
258
259
    /**
260
     * Calculate the check digit.
261
     *
262
     * @param $temporaryCodiceFiscale The first part of the codice fiscale.
263
     * @returns Returns the check digit part of the codice fiscale.
264
     */
265 17
    private function calculateCheckDigit($temporaryCodiceFiscale)
266
    {
267 17
        $sumEven = $this->calculateSumByDictionary($temporaryCodiceFiscale, $this->even, 1);
268 17
        $sumOdd = $this->calculateSumByDictionary($temporaryCodiceFiscale, $this->odd, 0);
269
270 17
        return chr(($sumOdd + $sumEven) % 26 + 65);
271
    }
272
273
    /**
274
     * Calculate the sum by the given dictionary for the given temporary codice fiscale.
275
     *
276
     * @param $temporaryCodiceFiscale The temporary codice fiscale.
277
     * @param $dictionaryArray The dictionary array.
278
     * @param $i The start index value.
279
     * @returns Returns the sum by the given dictionary for the given temporary codice fiscale.
280
     */
281 17
    private function calculateSumByDictionary($temporaryCodiceFiscale, $dictionaryArray, $i)
282
    {
283 17
        $sum = 0;
284 17
        for (; $i < 15; $i = $i + 2) {
285 17
            $k = $temporaryCodiceFiscale{$i};
286 17
            $sum = $sum + $dictionaryArray[$k];
287 17
        }
288
289 17
        return $sum;
290
    }
291
292
    /**
293
     * Calculate the omocodia case (additional translation).
294
     *
295
     * @param $temporaryCodiceFiscale The first part of the codice fiscale.
296
     * @returns Returns the new codice fiscale.
297
     */
298 17
    private function calculateOmocodia($temporaryCodiceFiscale)
299
    {
300 17
        if ($this->omocodiaLevel > 0) {
301 7
            $omocodiaLevelApplied = 0;
302 7
            for ($i = strlen($temporaryCodiceFiscale) - 1; $i > 0; --$i) {
303 7
                $k = $temporaryCodiceFiscale{$i};
304 7
                if ($omocodiaLevelApplied < $this->omocodiaLevel && is_numeric($k)) {
305 7
                    $newChar = $this->omocodiaCodes[$k];
306 7
                    $temporaryCodiceFiscale{$i}
307
                    = $newChar;
308 7
                    ++$omocodiaLevelApplied;
309 7
                }
310 7
            }
311 7
        }
312
313 17
        return $temporaryCodiceFiscale;
314
    }
315
}
316