PermissiveFactory::createAccount()   A
last analyzed

Complexity

Conditions 5
Paths 5

Size

Total Lines 36

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 5

Importance

Changes 0
Metric Value
cc 5
nc 5
nop 1
dl 0
loc 36
rs 9.0328
c 0
b 0
f 0
ccs 21
cts 21
cp 1
crap 5
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace byrokrat\banking;
6
7
use byrokrat\banking\Exception\InvalidAccountNumberException;
8
9
/**
10
 * Internal factory creating undefined account numbers (not belongning to a bank)
11
 *
12
 * It is permissive in the following respects:
13
 * - spaces ( ), hyphens (-) and dots (.) are ignored
14
 * - missplaced clearing-serial delimiters (,) are ignored
15
 *
16
 * For an alternative {@see StrictFactory}.
17
 * For a factory that validates account numbers {@see AccountFactory}.
18
 */
19
class PermissiveFactory implements AccountFactoryInterface
20
{
21
    private const CLEARING_SERIAL_DELIMITER = ',';
22
    private const IGNORED_CHARS = [' ', '-', '.'];
23
    private const CLEARING = 'CLEARING';
24
    private const CLEARING_CHECK = 'CLEARING_CHECK';
25
    private const SERIAL = 'SERIAL';
26
    private const CHECK = 'CHECK';
27
28 147
    public function createAccount(string $number): AccountNumber
29
    {
30
        $parts = [
31 147
            self::CLEARING => '',
32 147
            self::CLEARING_CHECK => '',
33 147
            self::SERIAL => '',
34 147
            self::CHECK => '',
35
        ];
36
37 147
        foreach (str_split($number) as $pos => $char) {
38 147
            if (in_array($char, self::IGNORED_CHARS)) {
39 89
                continue;
40
            }
41
42 147
            if ($char == self::CLEARING_SERIAL_DELIMITER) {
43 143
                self::terminateClearing($parts);
44 143
                continue;
45
            }
46
47 147
            if (!ctype_digit($char)) {
48 1
                throw new InvalidAccountNumberException(
49 1
                    "Invalid char ($char) at position $pos, expecting a digit"
50
                );
51
            }
52
53 146
            self::pushDigit($char, $parts);
54
        }
55
56 146
        return new UndefinedAccount(
57 146
            $number,
58 146
            $parts[self::CLEARING],
59 146
            $parts[self::CLEARING_CHECK],
60 146
            $parts[self::SERIAL],
61 146
            $parts[self::CHECK]
62
        );
63
    }
64
65
    /**
66
     * @param array<string, string> $parts
67
     */
68 146
    private static function pushDigit(string $digit, array &$parts): void
69
    {
70 146
        $pending = $parts[self::CHECK];
71 146
        $parts[self::CHECK] = $digit;
72
73 146
        if (strlen($parts[self::CLEARING]) < 4) {
74 146
            $parts[self::CLEARING] .= $pending;
75
        } else {
76 146
            $parts[self::SERIAL] .= $pending;
77
        }
78 146
    }
79
80
    /**
81
     * @param array<string, string> $parts
82
     */
83 143
    private static function terminateClearing(array &$parts): void
84
    {
85 143
        $pending = $parts[self::CHECK];
86 143
        $parts[self::CHECK] = '';
87
88 143
        if (strlen($parts[self::CLEARING]) < 4) {
89 135
            $parts[self::CLEARING] .= $pending;
90 9
        } elseif (!$parts[self::SERIAL]) {
91 8
            $parts[self::CLEARING_CHECK] = $pending;
92
        } else {
93 1
            $parts[self::CHECK] = $pending;
94
        }
95 143
    }
96
}
97