Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
98.04% |
50 / 51 |
|
75.00% |
3 / 4 |
CRAP | |
0.00% |
0 / 1 |
Iban | |
98.04% |
50 / 51 |
|
75.00% |
3 / 4 |
19 | |
0.00% |
0 / 1 |
isValid | |
95.83% |
23 / 24 |
|
0.00% |
0 / 1 |
8 | |||
validateZeros | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
4 | |||
validateNumeric | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
4 | |||
validateChecksum | |
100.00% |
11 / 11 |
|
100.00% |
1 / 1 |
3 |
1 | <?php |
2 | /** |
3 | * Jingga |
4 | * |
5 | * PHP Version 8.1 |
6 | * |
7 | * @package phpOMS\Validation\Finance |
8 | * @copyright Dennis Eichhorn |
9 | * @license OMS License 2.0 |
10 | * @version 1.0.0 |
11 | * @link https://jingga.app |
12 | */ |
13 | declare(strict_types=1); |
14 | |
15 | namespace phpOMS\Validation\Finance; |
16 | |
17 | use phpOMS\Validation\ValidatorAbstract; |
18 | |
19 | /** |
20 | * Iban validation. |
21 | * |
22 | * @package phpOMS\Validation\Finance |
23 | * @license OMS License 2.0 |
24 | * @link https://jingga.app |
25 | * @since 1.0.0 |
26 | */ |
27 | final class Iban extends ValidatorAbstract |
28 | { |
29 | /** |
30 | * {@inheritdoc} |
31 | */ |
32 | public static function isValid(mixed $value, array $constraints = null) : bool |
33 | { |
34 | if (!\is_string($value)) { |
35 | return false; |
36 | } |
37 | |
38 | $value = \str_replace(' ', '', \strtolower($value)); |
39 | |
40 | $temp = \substr($value, 0, 2); |
41 | if ($temp === false) { |
42 | return false; // @codeCoverageIgnore |
43 | } |
44 | |
45 | $enumName = 'C_' . \strtoupper($temp); |
46 | |
47 | if (!IbanEnum::isValidName($enumName)) { |
48 | self::$error = IbanErrorType::INVALID_COUNTRY; |
49 | |
50 | return false; |
51 | } |
52 | |
53 | /** @var string $enumVal */ |
54 | $enumVal = IbanEnum::getByName($enumName); |
55 | $layout = \str_replace(' ', '', $enumVal); |
56 | |
57 | if (\strlen($value) !== \strlen($layout)) { |
58 | self::$error = IbanErrorType::INVALID_LENGTH; |
59 | |
60 | return false; |
61 | } |
62 | |
63 | if (!self::validateZeros($value, $layout)) { |
64 | self::$error = IbanErrorType::EXPECTED_ZERO; |
65 | |
66 | return false; |
67 | } |
68 | |
69 | if (!self::validateNumeric($value, $layout)) { |
70 | self::$error = IbanErrorType::EXPECTED_NUMERIC; |
71 | |
72 | return false; |
73 | } |
74 | |
75 | if (!self::validateChecksum($value)) { |
76 | self::$error = IbanErrorType::INVALID_CHECKSUM; |
77 | |
78 | return false; |
79 | } |
80 | |
81 | return true; |
82 | } |
83 | |
84 | /** |
85 | * Validate positions that should have zeros |
86 | * |
87 | * @param string $iban Iban to validate |
88 | * @param string $layout Iban layout |
89 | * |
90 | * @return bool |
91 | * |
92 | * @since 1.0.0 |
93 | */ |
94 | private static function validateZeros(string $iban, string $layout) : bool |
95 | { |
96 | if (\strpos($layout, '0') === false) { |
97 | return true; |
98 | } |
99 | |
100 | $lastPos = 0; |
101 | while (($lastPos = \strpos($layout, '0', $lastPos)) !== false) { |
102 | if ($iban[$lastPos] !== '0') { |
103 | return false; |
104 | } |
105 | |
106 | ++$lastPos; |
107 | } |
108 | |
109 | return true; |
110 | } |
111 | |
112 | /** |
113 | * Validate positions that should be numeric |
114 | * |
115 | * @param string $iban Iban to validate |
116 | * @param string $layout Iban layout |
117 | * |
118 | * @return bool |
119 | * |
120 | * @since 1.0.0 |
121 | */ |
122 | private static function validateNumeric(string $iban, string $layout) : bool |
123 | { |
124 | if (\strpos($layout, 'n') === false) { |
125 | return true; |
126 | } |
127 | |
128 | $lastPos = 0; |
129 | while (($lastPos = \strpos($layout, 'n', $lastPos)) !== false) { |
130 | if (!\is_numeric($iban[$lastPos])) { |
131 | return false; |
132 | } |
133 | |
134 | ++$lastPos; |
135 | } |
136 | |
137 | return true; |
138 | } |
139 | |
140 | /** |
141 | * Validate checksum |
142 | * |
143 | * @param string $iban Iban to validate |
144 | * |
145 | * @return bool |
146 | * |
147 | * @since 1.0.0 |
148 | */ |
149 | private static function validateChecksum(string $iban) : bool |
150 | { |
151 | $chars = ['a' => 10, 'b' => 11, 'c' => 12, 'd' => 13, 'e' => 14, 'f' => 15, 'g' => 16, 'h' => 17, 'i' => 18, |
152 | 'j' => 19, 'k' => 20, 'l' => 21, 'm' => 22, 'n' => 23, 'o' => 24, 'p' => 25, 'q' => 26, 'r' => 27, |
153 | 's' => 28, 't' => 29, 'u' => 30, 'v' => 31, 'w' => 32, 'x' => 33, 'y' => 34, 'z' => 35,]; |
154 | $moved = \substr($iban, 4) . \substr($iban, 0, 4); |
155 | $movedArray = (array) \str_split($moved); |
156 | $new = ''; |
157 | |
158 | foreach ($movedArray as $key => $value) { |
159 | if (!\is_numeric($movedArray[$key])) { |
160 | $movedArray[$key] = $chars[$movedArray[$key]]; |
161 | } |
162 | |
163 | $new .= $movedArray[$key]; |
164 | } |
165 | |
166 | return \bcmod($new, '97') == 1; |
167 | } |
168 | } |