DEAlgorithm::isFollowRuleGermany1()   B
last analyzed

Complexity

Conditions 6
Paths 24

Size

Total Lines 27
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 20
c 1
b 0
f 0
nc 24
nop 1
dl 0
loc 27
rs 8.9777
1
<?php
2
3
namespace LeKoala\Tin\Algo;
4
5
use LeKoala\Tin\Util\StringUtil;
6
7
/**
8
 * Germany
9
 */
10
class DEAlgorithm extends TINAlgorithm
11
{
12
    const LENGTH_1 = 11;
13
    const PATTERN_1 = "[1-9]\\d{10}";
14
    const LENGTH_2 = 11;
15
    const PATTERN_2 = "[1-9]\\d{10}";
16
17
    public function validate(string $tin)
18
    {
19
        $normalizedTIN = str_replace("/", "", $tin);
20
        if (!$this->isFollowLength($normalizedTIN)) {
21
            return StatusCode::INVALID_LENGTH;
22
        }
23
        if ($this->isFollowLength($normalizedTIN) && !$this->isFollowPattern($normalizedTIN)) {
24
            return StatusCode::INVALID_PATTERN;
25
        }
26
        if (!$this->isFollowRules($normalizedTIN)) {
27
            return StatusCode::INVALID_SYNTAX;
28
        }
29
        return StatusCode::VALID;
30
    }
31
32
    public function isFollowLength(string $tin)
33
    {
34
        return $this->isFollowLength1($tin) || $this->isFollowLength2($tin);
35
    }
36
37
    public function isFollowLength1(string $tin)
38
    {
39
        return StringUtil::isFollowLength($tin, self::LENGTH_1);
40
    }
41
42
    public function isFollowLength2(string $tin)
43
    {
44
        return StringUtil::isFollowLength($tin, self::LENGTH_2);
45
    }
46
47
    public function isFollowPattern(string $tin)
48
    {
49
        return $this->isFollowPattern1($tin) || $this->isFollowPattern2($tin);
50
    }
51
52
    public function isFollowPattern1(string $tin)
53
    {
54
        if (!StringUtil::isFollowPattern($tin, self::PATTERN_1)) {
55
            return false;
56
        }
57
        $tab = [];
58
        $pos = [];
59
        for ($i = 0; $i < 10; $i++) {
60
            $tab[$i] = StringUtil::digitAt($tin, $i);
61
            $pos[$i] = 0;
62
        }
63
        for ($j = 0; $j < 10; $j++) {
64
            $pos[$tab[$j]]++;
65
        }
66
        $isEncounteredTwice2 = false;
67
        $isEncountered0 = false;
68
        for ($k = 0; $k < 10; $k++) {
69
            if ($pos[$k] == 2) {
70
                if ($isEncounteredTwice2) {
71
                    return false;
72
                }
73
                $isEncounteredTwice2 = true;
74
            }
75
            if ($pos[$k] == 0) {
76
                if ($isEncountered0) {
77
                    return false;
78
                }
79
                $isEncountered0 = true;
80
            }
81
        }
82
        return $isEncountered0;
83
    }
84
85
    public function isFollowPattern2(string $tin)
86
    {
87
        if (!StringUtil::isFollowPattern($tin, self::PATTERN_2)) {
88
            return false;
89
        }
90
        $tab = [];
91
        $pos = [];
92
        for ($i = 0; $i < 10; $i++) {
93
            $tab[$i] = StringUtil::digitAt($tin, $i);
94
            $pos[$i] = 0;
95
        }
96
        for ($i = 0; $i < 8; $i++) {
97
            if ($tab[$i] == $tab[$i + 1] && $tab[$i + 1] == $tab[$i + 2]) {
98
                return false;
99
            }
100
        }
101
        for ($j = 0; $j < 10; $j++) {
102
            $pos[$tab[$j]]++;
103
        }
104
        $isEncounteredTwice2 = false;
105
        $isEncounteredThrice3 = false;
106
        for ($k = 0; $k < 10; $k++) {
107
            if ($pos[$k] > 3) {
108
                return false;
109
            }
110
            if ($pos[$k] == 3) {
111
                if ($isEncounteredThrice3) {
112
                    return false;
113
                }
114
                $isEncounteredThrice3 = true;
115
            }
116
            if ($pos[$k] == 2) {
117
                if ($isEncounteredTwice2) {
118
                    return false;
119
                }
120
                $isEncounteredTwice2 = true;
121
            }
122
        }
123
        return $isEncounteredThrice3 || $isEncounteredTwice2;
124
    }
125
126
    public function isFollowRules(string $tin)
127
    {
128
        return (self::LENGTH_1 == strlen($tin) && $this->isFollowRuleGermany1($tin)) || $this->isFollowRuleGermany2($tin);
129
    }
130
131
    public function isFollowRuleGermany1(string $tin)
132
    {
133
        $c1 = StringUtil::digitAt($tin, 0);
134
        $c2 = [];
135
        for ($i = 0; $i < 9; $i++) {
136
            $c2[$i] = StringUtil::digitAt($tin, $i + 1);
137
        }
138
        $result = ($c1 + 10) % 10;
139
        if ($result == 0) {
140
            $result = 10;
141
        }
142
        $result *= 2;
143
        $x = $result % 11;
144
        for ($j = 0; $j < 9; $j++) {
145
            $x = ($x + $c2[$j]) % 10;
146
            if ($x == 0) {
147
                $x = 10;
148
            }
149
            $x *= 2;
150
            $x %= 11;
151
        }
152
        $c3 = StringUtil::digitAt($tin, 10);
153
        $total = 11 - $x;
154
        if ($total == 10) {
155
            return $c3 == 0;
156
        }
157
        return $total == $c3;
158
    }
159
160
    public function isFollowRuleGermany2(string $tin)
161
    {
162
        return StringUtil::digitAt($tin, 10) == $this->calculateCheckDigit($tin);
163
    }
164
165
    public function calculateCheckDigit(string $idnrString)
166
    {
167
        $ten = 10;
0 ignored issues
show
Unused Code introduced by
The assignment to $ten is dead and can be removed.
Loading history...
168
        $eleven = 11;
0 ignored issues
show
Unused Code introduced by
The assignment to $eleven is dead and can be removed.
Loading history...
169
        $chars = str_split($idnrString);
170
        $remainder_mod_ten = 0;
0 ignored issues
show
Unused Code introduced by
The assignment to $remainder_mod_ten is dead and can be removed.
Loading history...
171
        $remainder_mod_eleven = 10;
172
        $digit = 0;
173
        for ($length = strlen($idnrString), $counter = 0; $counter < $length - 1; $counter++) {
174
            $digit = intval($chars[$counter]);
175
            $remainder_mod_ten = ($digit + $remainder_mod_eleven) % 10;
176
            if ($remainder_mod_ten == 0) {
177
                $remainder_mod_ten = 10;
178
            }
179
            $remainder_mod_eleven = 2 * $remainder_mod_ten % 11;
180
        }
181
        $digit = 11 - $remainder_mod_eleven;
182
        if ($digit == 10) {
183
            $digit = 0;
184
        }
185
        return $digit;
186
    }
187
}
188