Completed
Push — master ( 5d18c7...cabcf3 )
by Hannes
16s queued 13s
created

PermissiveFactory::createAccount()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 36
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 5

Importance

Changes 0
Metric Value
cc 5
eloc 22
nc 5
nop 1
dl 0
loc 36
rs 8.439
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 146
    private static function pushDigit(string $digit, array &$parts): void
66
    {
67 146
        $pending = $parts[self::CHECK];
68 146
        $parts[self::CHECK] = $digit;
69
70 146
        if (strlen($parts[self::CLEARING]) < 4) {
71 146
            $parts[self::CLEARING] .= $pending;
72
        } else {
73 146
            $parts[self::SERIAL] .= $pending;
74
        }
75 146
    }
76
77 143
    private static function terminateClearing(array &$parts): void
78
    {
79 143
        $pending = $parts[self::CHECK];
80 143
        $parts[self::CHECK] = '';
81
82 143
        if (strlen($parts[self::CLEARING]) < 4) {
83 135
            $parts[self::CLEARING] .= $pending;
84 9
        } elseif (!$parts[self::SERIAL]) {
85 8
            $parts[self::CLEARING_CHECK] = $pending;
86
        } else {
87 1
            $parts[self::CHECK] = $pending;
88
        }
89 143
    }
90
}
91