StrictFactory::pushDigit()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 2
dl 0
loc 11
rs 9.9
c 0
b 0
f 0
ccs 7
cts 7
cp 1
crap 2
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 strict in the sense that if fails on any unexpected characters.
13
 *
14
 * For an alternative {@see PermissiveFactory}.
15
 * For a factory that validates account numbers {@see AccountFactory}.
16
 */
17
class StrictFactory implements AccountFactoryInterface
18
{
19
    private const CLEARING_SERIAL_DELIMITER = ',';
20
    private const CLEARING = 'CLEARING';
21
    private const CLEARING_CHECK = 'CLEARING_CHECK';
22
    private const SERIAL = 'SERIAL';
23
    private const CHECK = 'CHECK';
24
25 6
    public function createAccount(string $number): AccountNumber
26
    {
27
        $parts = [
28 6
            self::CLEARING => '',
29 6
            self::CLEARING_CHECK => '',
30 6
            self::SERIAL => '',
31 6
            self::CHECK => '',
32
        ];
33
34 6
        foreach (str_split($number) as $pos => $char) {
35 6
            if ($char == self::CLEARING_SERIAL_DELIMITER) {
36 4
                if (self::terminateClearing($parts)) {
37 2
                    continue;
38
                }
39
            }
40
41 6
            if (!ctype_digit($char)) {
42 3
                throw new InvalidAccountNumberException(
43 3
                    "Invalid char ($char) at position $pos, expecting a digit"
44
                );
45
            }
46
47 6
            self::pushDigit($char, $parts);
48
        }
49
50 3
        return new UndefinedAccount(
51 3
            $number,
52 3
            $parts[self::CLEARING],
53 3
            $parts[self::CLEARING_CHECK],
54 3
            $parts[self::SERIAL],
55 3
            $parts[self::CHECK]
56
        );
57
    }
58
59
    /**
60
     * @param array<string, string> $parts
61
     */
62 6
    private static function pushDigit(string $digit, array &$parts): void
63
    {
64 6
        $pending = $parts[self::CHECK];
65 6
        $parts[self::CHECK] = $digit;
66
67 6
        if (strlen($parts[self::CLEARING]) < 4) {
68 6
            $parts[self::CLEARING] .= $pending;
69
        } else {
70 4
            $parts[self::SERIAL] .= $pending;
71
        }
72 6
    }
73
74
    /**
75
     * @param array<string, string> $parts
76
     */
77 4
    private static function terminateClearing(array &$parts): bool
78
    {
79 4
        $pending = $parts[self::CHECK];
80 4
        $parts[self::CHECK] = '';
81
82 4
        if (strlen($parts[self::CLEARING]) == 3) {
83 1
            $parts[self::CLEARING] .= $pending;
84 1
            return true;
85
        }
86
87 3
        if (strlen($parts[self::CLEARING]) == 4 && !$parts[self::SERIAL]) {
88 1
            $parts[self::CLEARING_CHECK] = $pending;
89 1
            return true;
90
        }
91
92 2
        return false;
93
    }
94
}
95